いい質問があります。ソリューションには、おそらくいくつかのトレードオフがあります。最終的な答えは、プラットフォームに依存するという意味によって異なります。たとえば、外部アプリケーションを起動するプロセスを起動していて、アプリケーション間を切り替えるだけの場合、おそらくそれほど複雑にすることなく処理できます。ネイティブライブラリとP / Invokeを話している場合は、もう少しやらなければならないことがあります。ただし、1つのプラットフォームにのみ存在するライブラリとリンクする場合は、おそらく複数のアセンブリを使用する必要があります。
外部アプリ
#if
この状況では、おそらくステートメントを使用する必要はないでしょう。いくつかのインターフェースを設定するだけで、プラットフォームごとに1つの実装があります。ファクトリを使用してプラットフォームを検出し、適切なインスタンスを提供します。
場合によっては、特定のプラットフォーム用にコンパイルされた単なるバイナリですが、実行可能ファイルの名前とすべてのパラメーターは同じように定義されています。その場合、適切な実行可能ファイルを解決する必要があります。WindowsとLinuxで実行できるバルクオーディオコンバーターアプリの場合、バイナリ名を解決する静的イニシャライザーを使用しました。
public class AudioProcessor
{
private static readonly string AppName = "lame";
private static readonly string FullAppPath;
static AudioProcessor()
{
var platform = DetectPlatform();
var architecture = Detect64or32Bits();
FullAppPath = Path.combine(platform, architecture, AppName);
}
}
ここには何も派手なものはありません。昔ながらのクラス。
P / Invoke
P / Invokeは少し複雑です。一番下の行は、ネイティブライブラリの正しいバージョンがロードされていることを確認する必要があるということです。WindowsではP / Invokeを実行しSetDllDirectory()
ます。異なるプラットフォームでは、その手順は必要ない場合があります。だから、物事が乱雑になる場所です。あなたは使用する必要があり#if
、あなたの配布パッケージに含めている場合は特に-コールが自分のライブラリーのパスを解決し制御するために使用されるコントロールへのステートメントを。
完全に異なるプラットフォーム依存ライブラリへのリンク
ここでは、旧式のマルチターゲティングアプローチが役立つ場合があります。しかし、それは多くのugさを伴います。一部のプロジェクトが同じDLLをSilverlight、WPF、および潜在的にUAPをターゲットにしようとする時代には、異なるコンパイルタグでアプリケーションを複数回コンパイルする必要がありました。上記の各プラットフォームの課題は、同じ概念を共有しているにもかかわらず、プラットフォームが十分に異なるため、それらの違いを回避する必要があることです。これは私たちが地獄に入る場所です#if
。
このアプローチでは、.csproj
プラットフォーム依存の参照を処理するためにファイルを手動で編集する必要もあります。あなた以来.csproj
ファイルがMSBuildのファイルがあり、それが知られており、予測可能な方法で行うことが完全に可能です。
#地獄の場合
#if
ステートメントを使用してコードのセクションをオンまたはオフにできるため、アプリケーション間のわずかな違いを効果的に処理できます。表面的には、良いアイデアのように聞こえます。境界ボックスの視覚化をオンまたはオフにして、描画コードをデバッグする手段としても使用しました。
番号1の問題は、#if
ということであるどれもオフになっているコードがパーサーによって評価されていません。潜在的な構文エラー、またはさらに悪いことに、ライブラリの再コンパイルを待機する論理エラーが発生する場合があります。これは、コードのリファクタリングでさらに問題になります。メソッドの名前を変更したり、パラメーターの順序を変更したりするような単純なものは通常は正常に処理されますが、パーサーは#if
ステートメントによってオフにされたものを評価しないため、再コンパイルするまで表示されないコードが突然壊れます。
その方法で書かれた私のデバッグコードはすべて、一連のリファクタリングが壊れた後に書き直さなければなりませんでした。書き換え中に、グローバル構成クラスを使用してこれらの機能をオンまたはオフにしました。これにより、ツールのリファクタリングが可能になりましたが、APIが完全に異なる場合、このようなソリューションは役に立ちません。
私の好きな方法
学んだ多くの苦痛な教訓に基づいて、そしてマイクロソフト自身の例に基づいた私の好ましい方法は、複数のアセンブリを使用することです。
1つのコアNetStandardアセンブリは、すべてのインターフェイスを定義し、すべての共通コードを含みます。プラットフォーム依存の実装は、含まれる場合に機能を追加する別のアセンブリになります。
このアプローチは、新しい構成APIと現在のIDアーキテクチャによって実証されています。より具体的な統合が必要な場合は、それらの新しいアセンブリを追加するだけです。これらのアセンブリは、セットアップに自分自身を組み込むための拡張機能も提供します。依存性注入アプローチを使用している場合、これらの拡張メソッドにより、ライブラリはそのサービスを登録できます。
これは、#if
地獄を避け、実質的に異なる環境を満たすために私が知っている唯一の方法です。