.NET Standard,有哪些替代方案?






4.62/5 (4投票s)
如何支持 .NET 开发中的多个平台的说明
引言
许多开发人员都熟悉 .NET Standard。顾名思义,它是一个 .NET 标准,因为 .NET Framework 不是唯一支持的平台。创建 .NET Standard 项目使我们能够编写和编译代码以用于多个平台。支持哪些平台在一定程度上取决于您选择的 .NET Standard 版本,但通常的列表很长:.NET Core、.NET Framework、Mono、Xamarin.iOS、Xamarin.Mac、Xamarin.Android、通用 Windows 平台和 Unity。
如果您的代码确实是通用的,例如,如果您创建一个客户端包或实现一个数学模型,那很好。但是,一旦某些代码或 NuGet 包依赖于某些特定平台,.NET Standard 将无济于事。.NET Standard 是标准,而不是平台特定的。那么在这种情况下如何定位多个平台呢?
背景
如果您具有 .NET 开发经验,最好是具有多个平台或平台多个版本的经验,这将非常有帮助。
使用代码
第一步非常简单:在我们的项目文件中定位 TargetFrameworks
而不是 TargetFramework
。 因为这毕竟是我们想做的。
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;netcoreapp2.1</TargetFrameworks>
</PropertyGroup>
现在,我们开始编写一个虚拟类,看看它是否有效。
public class DummyClass : IAsyncDisposable
{
public void DoSomething()
{
Console.WriteLine("Something needs is done");
}
public void Dispose()
{
Console.WriteLine("Object is disposed");
}
public async ValueTask DisposeAsync()
{
Console.WriteLine("Object is disposed asynchronously");
}
}
我们在这里得到一个编译器错误
引用错误 CS0246:找不到类型或命名空间名称“IAsyncDisposable”(是否缺少 using 指令或程序集引用?)
错误是正确的。我们试图在非最新的 .NET Core 版本中定位最新的 .NET 功能。但是对于 .NET Core 3.1,不应该发生这样的错误。我们需要在我们的代码中解释哪个部分是平台特定的
public class DummyClass
#if NETCOREAPP3_1
: IAsyncDisposable
#endif
{
public void DoSomething()
{
Console.WriteLine("Something needs is done");
}
public void Dispose()
{
Console.WriteLine("Object is disposed");
}
#if NETCOREAPP3_1
public async ValueTask DisposeAsync()
{
Console.WriteLine("Object is disposed asynchronously");
}
#endif
}
通过使用 #if
指令,可以清楚地看出某些代码是 .NET Core 3.1 特定的。其他代码可以为这两个平台编译。
添加特定于平台的 NuGet 包也是可能的。 它是这样工作的
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="2.1.3" />
</ItemGroup>
添加 Condition
可确保为正确的平台解析正确的依赖项。 这种方法有效,但存在问题
- 您需要手动更改您的 csproj 文件。
- 您添加的平台越多,您的 csproj 文件就越复杂。
- 您添加的平台越多,您的代码就越复杂。
从可维护性的角度来看,这可能不是你想要的情况。这就是为什么需要解释这种方法的替代方案:具有部分类的共享项目。第一步是创建一个共享项目,这只是 Visual Studio 中可用的一种项目类型。在共享项目中,我们添加一个部分类,该类仅包含希望为我们想要定位的每个平台编译的代码。这是它
public partial class DummyClass : IDisposable
{
public void DoSomething()
{
Console.WriteLine("Something needs is done");
}
public void Dispose()
{
Console.WriteLine("Something is disposed");
}
}
共享项目无法编译,但共享项目中的代码可以编译。因此,我们需要在我们的 .NET Core 2.1 项目中引用它。如果您以前从未使用过共享项目,这是您添加此类引用的方式。.NET Core 2.1 项目可以编译,因为 IAsyncDisposable
接口不存在。但是对于 .NET Core 3.1 项目,我们需要它。因此,我们也在这里添加一个部分类。
public partial class DummyClass : IAsyncDisposable
{
public async ValueTask DisposeAsync()
{
Console.WriteLine("Object is disposed asynchronously");
}
}
看来我们刚刚解决了提到的问题
- 无需手动更改项目文件。
- 更多平台仅意味着更多项目。 项目文件都很简单。
- 代码文件简短而简单
但是,这种方法也存在问题:需要维护更多项目。 此外,您无法从共享项目生成 NuGet 包。如果您对这两种解决方案的工作示例感兴趣,请从 GitHub 下载代码。
关注点
共享项目在 Xamarin 开发领域尤其受欢迎,但也可以用于其他目的。 在许多公司,初级开发人员被告知复制代码是不好的,因为它会使维护代码更加困难。 但是,我们经常忘记告诉他们的是,共享项目具有相同的效果(相同的代码在不同的项目中编译),但没有其缺点(代码只需要在一个项目的一部分的一个文件中维护)。 我成了共享项目的忠实粉丝。 但是,正如我提到的,每个解决方案都有缺点。
历史
- 2020 年 5 月 16 日:初始版本