65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.62/5 (4投票s)

2020年5月19日

CPOL

4分钟阅读

viewsIcon

7717

如何支持 .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 日:初始版本
© . All rights reserved.