有什么方法可以制作Windows和Mac的.Net库,并具有依赖于平台的引用?


12

我们正在尝试使用C#开发用于台式机(Windows和Mac)的跨平台应用程序。该应用程序包含大量与平台相关的内容,我们的团队负责人希望我们用C#编写所有这些代码。做到这一点的简单方法是用C ++编写包装程序,然后只引用C#代码中的库,然后让C ++库处理平台方面的东西……可惜,事实并非如此。

我正在尝试使用Visual Studio for Mac建立一个库。我希望能够在单个库中提供单个接口,以将对本机文本的访问权限授予当前平台上的语音库-最好不进行两个程序集。Mac上不提供Windows版C#中的文本到语音库,因此我们除了提供自己的桥梁之外,没有其他选择。

我很高兴让该库执行操作系统查找以确定它运行在哪个操作系统上和/或尝试加载一堆本机库并仅使用将加载的库。

如果必须的话,必须有一个可以让我编写两个实现的项目。我还没有找到在Visual Studio for Mac中创建项目的方法,但是允许我这样做。唯一允许多种实现的项目类型似乎要求这些实现是针对iOS还是针对Android。


您将使用什么.NET运行时?
欣快的

2
Xamarin做类似的事情。也许与构建配置?
伊万

2
值得注意的是,Visual Studio for Mac并不是真正的Visual Studio,它只是经过重新包装的Xamarin Studio。Xamarin文档中可能有内容吗?
richzilla '18

3
最好的方法是使用不同的程序集...一种用于接口和通用代码,另一种用于目标平台。但是,您可以多目标并使用#if语句换出实现。不利的一面是,未启用的所有内容都不会进行评估,因此很容易过时。
Berin Loritsch '18

当您说平台相关的引用时,您是在说本机代码还是所有C#代码?
Berin Loritsch '18

Answers:


4

你有个好问题。您的解决方案可能需要权衡取舍。最终答案确实取决于您依赖平台的含义。例如,如果您正在启动一个进程以启动外部应用程序,而您只是在一个应用程序和另一个应用程序之间切换,则可能无需太多麻烦就可以处理它。如果您正在与本机库进行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地狱并满足截然不同的环境的方法。


我在C#上很烂(已经使用C ++大约18年了,使用C#大约半天了,这是可以预料的)。这个新的配置API ...您能提供一些链接吗?我似乎无法独自找到它。
清晰的

2
docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration / ... 有多个Nuget软件包可添加其他配置源。例如,从环境变量,JSON文件等
帐户berin Loritsch

1
这是一Microsoft.Extensions.Configuration组API。
Berin Loritsch '18

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.