你有个好问题。您的解决方案可能需要权衡取舍。最终答案确实取决于您依赖平台的含义。例如,如果您正在启动一个进程以启动外部应用程序,而您只是在一个应用程序和另一个应用程序之间切换,则可能无需太多麻烦就可以处理它。如果您正在与本机库进行P / Invoke对话,那么还有很多事情要做。但是,如果要链接到仅存在于一个平台上的库,则可能需要使用多个程序集。
外部应用
#if
在这种情况下,您可能不需要使用语句。只需设置一些接口,并在每个平台上实现一个即可。使用工厂来检测平台并提供正确的实例。
在某些情况下,它只是为特定平台编译的二进制文件,但是可执行文件的名称和所有参数均定义为相同。在这种情况下,解决正确的可执行文件即可。对于可以在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 /调用
P / Invoke有点棘手。最重要的是,您需要确保已加载正确版本的本机库。在Windows上,您可以P / Invoke SetDllDirectory()
。不同的平台可能不需要该步骤。因此,这里可能会变得凌乱。您可能需要使用#if
语句来控制使用哪个调用来控制解析库路径,特别是如果您将其包含在分发包中。
链接到完全不同的平台相关库
老式的多目标方法可能在这里有用。但是,它确实带有很多丑陋之处。在某些项目试图具有相同的DLL目标Silverlight,WPF和潜在的UAP的日子里,您将不得不使用不同的编译标签多次编译应用程序。上述每个平台所面临的挑战是,尽管它们共享相同的概念,但是这些平台之间的差异足够大,因此您必须解决这些差异。这是我们进入地狱的地方#if
。
这种方法还需要手动编辑.csproj
文件以处理平台相关的引用。由于您的.csproj
文件是MSBuild文件,因此完全有可能以已知且可预测的方式进行操作。
#if地狱
您可以使用#if
语句打开和关闭代码段,这样可以有效地处理应用程序之间的细微差别。从表面上看,这听起来是个好主意。我什至使用它作为打开和关闭边界框可视化以调试图形代码的手段。
数字1问题#if
是,没有被断开时由解析器评估的代码。您可能有潜在的语法错误,或更糟糕的是,逻辑错误等待您重新编译该库。对于重构代码,这甚至变得更加成问题。诸如重命名方法或更改参数顺序之类的简单操作通常可以解决,但是由于解析器从不评估该#if
语句关闭的任何内容,因此您突然破坏了重新编译之前看不到的代码。
在一系列重构破坏了我的调试代码之后,所有以这种方式编写的调试代码都必须重写。在重写期间,我使用了全局配置类来打开和关闭这些功能。这样就可以重构工具证明,但是当API完全不同时,这种解决方案就无济于事。
我的首选方法
基于许多痛苦的经验,甚至基于Microsoft自己的示例,我的首选方法是使用多个程序集。
一个核心NetStandard程序集将定义所有接口并包含所有通用代码。平台相关的实现将在单独的程序集中,当包含时会添加功能。
新的Configuration API和当前的Identity体系结构例证了这种方法。当您需要更具体的集成时,只需添加这些新程序集。这些程序集还提供扩展功能,以将其自身合并到您的设置中。如果您使用的是依赖项注入方法,则这些扩展方法允许该库注册其服务。
这是我所知道的唯一避免#if
地狱并满足截然不同的环境的方法。