乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 重新认识.Net、.Net Core、.Net Standard、.Net Framework、Mono的关系

什么是.Net

.NET是一个免费的跨平台开放源代码开发人员平台,用于生成多种类型的应用程序。.NET基于许多大规模应用在生产中使用的高性能运行时构建而来。

image

.NET是由微软维护的最新通用开发平台,其前身是.Net Core。它可以在不同的平台上工作,并以一种使.NET快速、灵活和现代的方式进行了重新设计。这恰好是微软的主要贡献之一。开发人员现在可以用.NET构建安卓、iOS、Linux、Mac和Windows应用程序,而且都是开源的,使用MIT和Apache 2许可证,是.NET Foundation的项目。.NET支持三种语言:C#F#Visual Basic

.NET实现

.NET应用是针对一个或多个.NET实现开发的。.NET实现包括.NET Framework、.NET 5(和.NET Core)以及Mono。

.NET的每个实现都具有以下组件:

  • 一个或多个运行时,例如.NET Framework CLR和.NET 5 CLR。
  • 类库,例如.NET Framework基类库和.NET 5基类库。
  • (可选)一个或多个应用程序框架,例如ASP.NET、Windows Forms和Windows Presentation Foundation(WPF)都包括在.NET Framework和.NET 5+中。
  • 可包含开发工具。某些开发工具在多个实现之间共享。

Microsoft支持以下四种.NET实现:

  • .NET 5(和.NET Core)及更高版本
  • .NET Framework
  • Mono
  • UWP

.NET 6现在是主要的实现,是正在进行的开发的重点。.NET 6是基于单个代码基底进行构建的,该代码基底支持多个平台和许多工作负载,例如Windows桌面应用和跨平台控制台应用、云服务和网站。某些工作负载(例如.NET Web Assembly生成工具)作为可选安装提供。

.NET 5及更高版本

https://docs.microsoft.com/zh-cn/dotnet/core/

.NET 5+(以前称为.NET Core)是.NET的跨平台实现,专用于处理大规模的服务器和云工作负载。它还支持其他工作负载,包括桌面应用。可在Windows、macOS和Linux上运行。它可实现.NET Standard,因此面向.NET Standard的代码都可在.NET 5+上运行。ASP.NET Core、Windows窗体和Windows Presentation Foundation(WPF)都在.NET 5+上运行。

.NET 6是此.NET实现的最新版本。

.NET Framework

https://docs.microsoft.com/zh-cn/dotnet/framework/

.Net Framework是自2002年起就已存在的原始.NET实现4.5版以及更高版本实现.NET Standard,因此面向.NET Standard的代码都可在这些版本的.NET Framework上运行。它还包含一些特定于Windows的API,如通过Windows窗体和WPF进行Windows桌面开发的API。.NET Framework非常适合用于生成Windows桌面应用程序

Mono

https://www.mono-project.com/docs/

Mono是主要在需要小型运行时使用的.NET实现。它是在Android、macOS、iOS、tvOS和watchOS上驱动Xamarin应用程序的运行时,且主要针对小内存占用。Mono还支持使用Unity引擎生成的游戏

它支持所有当前已发布的.NET Standard版本。

以前,Mono实现更大的.NET Framework API并模拟一些Unix上最常用的功能。有时使用它运行依赖Unix上的这些功能的.NET应用程序。

Mono通常与实时编译器一起使用,但它也提供在iOS之类的平台使用的完整静态编译器(预先编译)。

通用Windows平台(UWP)

https://docs.microsoft.com/zh-cn/windows/uwp/get-started/universal-application-platform-guide

UWP是用于为物联网(IoT)生成新式触控Windows应用程序和软件的.NET实现。它旨在统一可能想要以其为目标的不同类型的设备,包括电脑、平板电脑、电话,甚至Xbox。UWP提供许多服务,如集中式应用商店、执行环境(App Container)和一组Windows API(用于代替Win32(WinRT))。应用可采用C++、C#、Visual Basic和JavaScript编写。

.NET类库

类库是.NET的共享库概念。通过类库可将实用功能组件化为可供多个应用程序使用的模块。还可使用类库加载应用程序启动时不需要或未知的功能。类库通过.NET程序集文件格式进行描述。

有三种类型的类库可供使用:

  • 平台特定的类库可访问给定平台(例如,Windows上的.NET Framework、Xamarin、iOS)中的所有API,但只有面向该平台的应用和库可使用该类库。
  • 可移植类库可访问API的子集,并且可供面向多个平台的应用和库使用。
  • .NET Standard类库将平台专用库概念和可移植库概念合并到一个模型中,以同时获取两方面的优势。

平台特定的类库

平台特定的类库绑定到单个.NET平台(例如,Windows上的.NET Framework),因此它可以在已知的执行环境上接收重要的依赖项。此类环境会公开一组已知API(.NET和OSAPI),维护并公开预期状态(例如,Windows注册表)。

创建平台特定的库的开发人员可充分利用基础平台。该库只会在给定平台上运行,因此不需要平台检查和其他形式的条件代码(针对多个平台取模单个源代码)。

平台特定的库一直是.NET Framework的主要类库类型。即使出现了其他.NET实现,平台特定的库也仍然是最主要的类库类型。

可移植类库

可移植库支持多个.NET实现。该库仍可在已知执行环境上接收依赖项,不过该环境是一种合成环境,由一组具体.NET实现的交集生成。公开的API和平台假设是平台特定的库可用的子集。

创建可移植库时,需选择平台配置。平台配置是需要支持的平台集(例如,.NET Framework 4.5+、Windows Phone 8.0+)。要支持的平台越多,可生成的API和平台假设就越少,公分母越小。这一特性可能最初会令人感到疑惑,因为人们常认为“越多越好”,但却发现更多的支持平台带来的可用API更少

许多库开发人员已经从由一个源开发多个平台特定的库(使用条件编译指令)转向开发可移植库。有多种方法可在可移植库中访问平台特定的功能,其中“诱饵替换”是目前最广为接受的方法。

.NET Standard类库

https://docs.microsoft.com/zh-cn/dotnet/standard/net-standard

.NET Standard库是平台特定的库和可移植库概念的替代。.NET Core库可从基础平台公开所有功能(无合成平台或平台交集),就此而言,它是平台特定的库。该库可在所有支持平台上运行,就此而言,它是可移植库。

.NET Standard公开一组库协定。.NET实现必须完全支持每个协定,否则就全都不支持。因此,每个实现都支持一组.NET Standard协定。得出的必然结果是,.NET Standard类库在支持其协定依赖项的平台上受到支持。

.NET Standard不公开整个.NET Framework的功能(也不将此作为目标),但相比可移植类库,库公开的API更多。

以下实现支持.NET Standard库:

  • .NET Core
  • .NET Framework
  • Mono
  • 通用Windows平台(UWP)

Mono类库

Mono支持多种类库,包括上述三种类型的库。Mono通常被视为.NET Framework的跨平台实现。部分原因是,平台特定的.NET Framework库可在Mono运行时上运行,而无需修改或重新编译。创建可移植库之前,此特性就已存在,因此在.NET Framework和Mono之间启用二进制可移植性是显而易见的选择(虽然它只能单向运行)

.NET Standard

.NET Standard是针对多个.NET实现推出的一套正式的.NET API规范。推出.NET Standard的背后动机是要提高.NET生态系统中的一致性。.NET 5及更高版本采用不同的方法来建立一致性,这种方法在大多数情况下都不需要.NET Standard。但如果要在.NET Framework和其他任何.NET实现(例如.NET Core)之间共享代码,则库必须面向.NET Standard 2.0。不会发布新版本的.NET Standard,但.NET 5、.NET 6以及所有将来的版本将继续支持.NET Standard 2.1及更早版本

.NET Standard版本

已对.NET Standard进行版本控制。每个新版本都会添加更多API。当库是针对.NET Standard的某个版本生成时,它可以在任何实现该版本(或更高版本)的.NET Standard的.NET实现上运行。

面向更高版本的.NET Standard让库能够使用更多API,但这意味着它只能在较新版本的.NET上使用。面向较低版本会减少可用的API,但意味着库可以在更多位置运行。

.NET Standard 1.0提供37,118个可用API中的7,949个

.NET 实现 版本支持
.NET 和.NET Core 1.0、1.1、2.0、2.1、2.2、3.0、3.1、5.0、6.0
.NET Framework 4.5、4.5.1、4.5.2、4.6、4.6.1、4.6.2、4.7、4.7.1、4.7.2、4.8
Mono 4.6、5.4、6.4
Xamarin.iOS 10.0、10.14、12.16
Xamarin.Mac 3.0、3.8、5.16
Xamarin.Android 7.0、8.0、10.0
通用 Windows 平台 8.0、8.1、10.0、10.0.16299,待定
Unity 2018 年 1 月

.NET Standard 2.0提供37,118个可用API中的32,638个

.NET 实现 版本支持
.NET 和.NET Core 2.0、2.1、2.2、3.0、3.1、5.0、6.0
.NET Framework 1 4.6.1 2、4.6.2、4.7、4.7.1、4.7.2、4.8
Mono 5.4、6.4
Xamarin.iOS 10.14、12.16
Xamarin.Mac 3.8、5.16
Xamarin.Android 8.0、10.0
通用 Windows 平台 10.0.16299,待定
Unity 2018 年 1 月

.NET Standard 2.1提供37,118个可用API中的37,118个

.NET 实现 版本支持
.NET 和.NET Core 3.0、3.1、5.0、6.0
.NET Framework 1 N/A2
Mono 6.4
Xamarin.iOS 12.16
Xamarin.Mac 5.16
Xamarin.Android 10.0
通用 Windows 平台 待定
Unity 2021.2

要定位哪个.NET Standard版本

建议定位.NET Standard 2.0,除非你需要支持早期版本。最常规用途的库应该不需要除.NET Standard 2.0之外的其他API。所有新式平台都支持.NET Standard 2.0,并且它是支持具有一个目标的多个平台的推荐方法。

如果需要定位.NET Standard 1.x,建议还定位.NET Standard 2.0。.NET Standard 1.x作为一组精细的NuGet包分发,它创建了一个大型的包依赖项关系图,并导致开发人员在构建时下载大量的包。

NET Standard版本控制规则

版本控制规则主要有两个:

  • 累加性:.NET Standard版本在逻辑上形成同心圆。也就是说,较高的版本包含较低版本的所有API。版本之间没有重大更改。
  • 不可变:一旦发布,.NET Standard版本就会冻结起来。

在.NET Standard 2.1版本之后,将不会有新版本

.NET Framework兼容性模式

从.NET Standard 2.0开始,引入了.NET Framework兼容性模式。此兼容性模式允许.NET Standard项目引用.NET Framework库,就像其针对.NET Standard编译一样。引用.NET Framework库并不适用于所有项目,,例如使用Windows Presentation Foundation(WPF)API的库。

.NET Standard与.NET 5+

问题1:.NET Standard太慢

.NET Standard是在.NET平台在实现层面上没有融合的时候设计的。这使得编写需要在不同环境下工作的代码变得困难,因为不同的工作负载使用不同的.NET实现。

.NET Standard的目标是统一基础类库(BCL)的功能集,这样你就可以编写一个可以在任何地方运行的单一库。这对我们很有帮助:超过77%的前1000个软件包都支持.NET标准。如果我们看一下NuGet.org上所有在过去6个月内更新过的软件包,采用率为58%。

image

但是,单单对API集进行标准化就会产生一个税。每当我们增加新的API时,它都需要协调--这一直在发生。.NET开源社区(包括.NET团队)通过提供新的语言功能、可用性改进、新的跨领域功能(例如,支持新的数据格式或网络协议),不断在BCL中进行创新。

而我们可以作为NuGet包提供新的类型,但我们不能通过这种方式在现有类型上提供新的API。因此,从一般意义上讲,BCL的创新需要运送新版本的.NET标准。

直到.NET Standard 2.0,这并不是一个真正的问题,因为我们只对现有的API进行标准化。但在.NET Standard 2.1中,我们对全新的API进行了标准化,这就是我们看到的相当多的摩擦

这种摩擦来自哪里?

.NET Standard是一个所有.NET实现都必须支持的API集,所以它有一个编辑方面的问题,即所有API都必须由.NET Standard审查委员会审查。该委员会由.NET平台实施者以及.NET社区的代表组成。我们的目标是只对那些我们可以在所有当前和未来的.NET平台上真正实现的API进行标准化。这些审查是必要的,因为有不同的.NET堆栈的实现,有不同的约束。

我们预测到了这种类型的摩擦,这就是为什么我们很早就说.NET Standard将只对那些已经在至少一个.NET实现中运做的API进行标准化。这起初看起来很合理,但后来你会发现,.NET Standard不可能经常发货。所以,如果一个功能错过了某个特定的版本,你可能要等上几年才能得到它,甚至可能要等上更长时间,直到这个版本的.NET标准被广泛支持。

我们觉得对于某些功能来说,机会损失太大,所以我们做了一些不自然的行为来规范那些还没有发货的API(比如)。对所有的功能都这样做简直是太昂贵了,这就是为什么相当多的功能仍然错过了.NET Standard 2.1的列车(比如新的硬件内在因素)。IAsyncEnumerable<T>

但是,如果有一个单一的代码库呢?如果这个代码库必须支持所有使.NET实现与众不同的方面,例如支持即时编译(JIT)提前编译(AOT)呢?

我们不是在事后才做这些审查,而是从一开始就把所有这些方面作为功能设计的一部分。在这样一个世界里,标准化的API集在结构上就是通用API集。当一个功能被实现时,由于代码库是共享的,它已经可以为所有人所用。

问题2:.NET Standard需要一个解码器环

将API集与它的实现分开,不仅会减慢API的可用性。它还意味着我们需要将.NET Standard版本与它们的实现进行映射。作为一个不得不长期向许多人解释这个表格的人,我已经体会到这个看似简单的想法是多么复杂。我们已经尽力让它变得更简单,但最终,这只是固有的复杂性,因为API集和实现是独立运输的。

我们通过在它们下面添加另一个代表通用API集的合成平台,统一了.NET平台。在一个非常现实的意义上,这幅受XKCD启发的漫画是正确的。

image

如果不真正合并我们层图中的一些矩形,我们就无法解决这个问题,这就是.NET 5所做的:它提供了一个统一的实现,所有各方都建立在相同的基础上,从而获得相同的API形状和版本号

问题3:.NET Standard暴露了特定平台的API

当我们设计.NET Standard时,我们不得不做出务实的让步,以避免过多地破坏库的生态系统。也就是说,我们不得不包括一些Windows专用的API(如文件系统ACL、注册表、WMI等)。今后,我们将避免在未来的版本中加入特定平台的API。然而,我们不可能预测未来。例如,对于Blazor WebAssembly,我们最近增加了一个新的环境,在这个环境中,.NET的运行和一些本来跨平台的API(如线程或进程控制)不能在浏览器的沙盒中得到支持。net5.0 net6.0

你们中的许多人都抱怨说,这些API感觉就像"地雷"--代码在编译时没有错误,因此看起来可以移植到任何平台上,但当在一个没有给定API实现的平台上运行时,你会得到运行时错误。

从.NET 5开始,我们在SDK中提供了分析器和代码修复器,这些都是默认的。这包括平台兼容性分析器,它可以检测出无意中使用了不被你打算运行的平台所支持的API的情况。这个功能取代了NuGet包 Microsoft.DotNet.Analyzers.Compatibility

让我们先来看看Windows特有的API

处理与Windows特定的API

当你创建一个以net5.0为目标的项目时,你可以引用这个包。但当你开始使用它时,你会得到以下警告:Microsoft.Win32.Registry

private static string GetLoggingDirectory()
{
    using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Fabrikam"))
    {
        if (key?.GetValue("LoggingDirectoryPath") is string configuredPath)
            return configuredPath;
    }

    string exePath = Process.GetCurrentProcess().MainModule.FileName;
    string folder = Path.GetDirectoryName(exePath);
    return Path.Combine(folder, "Logging");
}
CA1416: 'RegistryKey.OpenSubKey(string)' is supported on 'windows'
CA1416: 'Registry.CurrentUser' is supported on 'windows'
CA1416: 'RegistryKey.GetValue(string?)' is supported on 'windows'

对于如何处理这些警告,你有三个选择:

  • 保护调用(Guard the call)。你可以通过使用OperatingSystem.IsWindows()在调用API之前检查你是否运行在Windows上。

  • 将调用标记为Windows专用。在某些情况下,通过[SupportedOSPlatform("windows")]将调用成员标记为特定平台可能是合理的。

  • 删除这段代码(Delete the code)。一般来说,这不是你想要的,因为这意味着当你的代码被Windows用户使用时,你会失去保真度,但对于存在跨平台替代方案的情况,你可能最好使用它而不是平台特定的API。例如,你可以使用一个XML配置文件,而不是使用注册表。

  • 抑制警告(Suppress the warning)。你当然可以作弊,简单地抑制警告,可以通过或.NET来实现。 然而,在使用特定平台的API时,你应该更喜欢选项(1)和(2)。

为了保护调用,请使用System.OperatingSystem类上的新静态方法,例如:

private static string GetLoggingDirectory()
{
    if (OperatingSystem.IsWindows())
    {
        using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Fabrikam"))
        {
            if (key?.GetValue("LoggingDirectoryPath") is string configuredPath)
                return configuredPath;
        }
    }

    string exePath = Process.GetCurrentProcess().MainModule.FileName;
    string folder = Path.GetDirectoryName(exePath);
    return Path.Combine(folder, "Logging");
}

要将你的代码标记为Windows专用,应用新的属性:SupportedOSPlatform:

[SupportedOSPlatform("windows")]
private static string GetLoggingDirectory()
{
    using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Fabrikam"))
    {
        if (key?.GetValue("LoggingDirectoryPath") is string configuredPath)
            return configuredPath;
    }

    string exePath = Process.GetCurrentProcess().MainModule.FileName;
    string folder = Path.GetDirectoryName(exePath);
    return Path.Combine(folder, "Logging");
}

在这两种情况下,使用注册表的警告都会消失。

关键的区别是,在第二个例子中,分析器现在会对调用网站发出警告,因为它现在被认为是一个Windows特定的API。换句话说,你把做平台检查的要求转发给你的调用者。GetLoggingDirectory()

该属性可以应用于成员、类型或汇编级别。这个属性也被BCL本身所使用。例如,程序集应用了这个属性,这就是分析器首先知道注册表是Windows特定API的原因。[SupportedOSPlatform]Microsoft.Win32.Registry

注意,如果你的目标是 ,这个属性会自动应用到你的程序集上。这意味着从使用Windows特定的API将永远不会产生任何警告,因为你的整个程序集被认为是Windows特定的net5.0-windows

.NET 5是.NET Standard和.NET Core的组合

.NET 5和后续版本将是一个单一的代码库,支持桌面应用、移动应用、云服务、网站,以及.NET明天将运行的任何环境。

你可能会想"等等,这听起来不错,但如果有人想创建一个全新的实现呢"。这也很好。但几乎没有人会从头开始创建一个。最有可能的是,它将是当前代码基础(dotnet/runtime)的一个分叉。例如,Tizen(三星的智能电器平台)使用的是.NET核心,改动很小,上面有三星特有的应用模型。

分叉保留了合并关系,这使得维护者可以继续从dotnet/runtime repo拉入新的变化,在未受其变化影响的领域从BCL创新中获益。这与Linux发行版的工作方式非常相似。

当然,在有些情况下,人们可能想创建一个非常不同的.NET"种类",比如一个没有当前BCL的最小运行时。但这意味着它不能利用现有的.NET库生态系统,这意味着它也不能实现.NET标准。一般来说,我们对追求这个方向不感兴趣,但.NET标准和.NET核心的融合并没有阻止这一点,也没有使它变得更难。

.NET版本划分

作为一个库的作者,你可能想知道.NET 5何时会被广泛支持。今后,我们将在每年的11月发布.NET,每隔一年发布一个长期支持(LTS)版本。

.NET 5将于2020年11月发布,.NET 6将于2021年11月作为LTS发布。我们创建这个固定的时间表是为了让你更容易计划你的更新(如果你是一个应用程序开发者)和预测对支持的.NET版本的需求(如果你是一个库的开发者)。

由于能够并排安装.NET Core,新版本的采用速度相当快,LTS版本最受欢迎。事实上,.NET Core 3.1是有史以来采用最快的.NET版本

image

我们的期望是,每次发货时,我们把所有的框架名称连在一起发货。例如,它可能看起来像这样。

.NET 5 .NET 6 .NET 7
net5.0 net6.0 net7.0
net6.0-android net7.0-android
net6.0-ios net7.0-ios
net5.0-windows net6.0-windows net7.0-windows
net5.0-someoldos

这意味着你通常可以期待,无论我们在BCL中做了什么创新,你都能从所有的应用模型中使用它,无论它们在哪个平台上运行。这也意味着,只要你运行最新版本的库,最新框架的库总是可以从所有应用模型中使用。

这种模式消除了围绕.NET标准版本的复杂性,因为每次我们发货时,你可以假设所有平台都会立即完全支持新版本。我们通过使用前缀命名惯例来巩固这一承诺。

.NET的新版本可能会增加对其他平台的支持。例如,我们将通过.NET 6增加对Android和iOS的支持。反之,我们可能会停止支持那些不再相关的平台。在.NET 6中不存在的假装目标框架就说明了这一点。我们没有放弃一个平台的计划,但这个模型支持它。这将是一个大问题,不是预期的,而且会提前很长时间宣布。这与我们在.NET Standard中的模式是一样的,例如,没有新版本的Windows Phone来实现后来的.NET标准。

目标框架(TFM)

目标框架(Target Framework Moniker, TFM)是一个标准化令牌格式,用于指定.NET应用或库的目标框架。目标框架通常由短名称(如net462)引用。存在长格式的TFM(如.NET Framework,Version=4.6.2),但通常不用来指定目标框架。

目标框架 最新稳定版本 目标框架名字对象 (TFM) 已实现.NET Standard 版本
.NET 6 6 net6.0 2.1
.NET 5 5 net5.0 2.1
.NET Standard 2.1 netstandard2.1 空值
.NET Core 3.1 netcoreapp3.1 2.1
.NET Framework 4.8 net48 2

.NET 5及更高版本特定于OS的TFM

TFM 可兼容对象
net5.0 net1..4(带有 NU1701 警告)
netcoreapp1..3.1 (引用 WinForms 或 WPF 时出现警告)
netstandard1..2.1
net5.0-windows netcoreapp1..3.1(以及从 net5.0 继承的所有其他内容)
net6.0 (后续版本的 net5.0)
net6.0-android xamarin.android(以及从 net6.0 继承的所有其他内容)
net6.0-ios xamarin.ios(以及从 net6.0 继承的所有其他内容)
net6.0-maccatalyst xamarin.ios(以及从 net6.0 继承的所有其他内容)
net6.0-macos xamarin.mac(以及从 net6.0 继承的所有其他内容)
net6.0-tvos xamarin.tvos(以及从 net6.0 继承的所有其他内容)
net6.0-windows (后续版本的 net5.0-windows)
net7.0 (后续版本的 net6.0)
net7.0-android (后续版本的 net6.0-android)
net7.0-ios (后续版本的 net6.0-ios)
net7.0-maccatalyst (后续版本的 net6.0-maccatalyst)
net7.0-macos (后续版本的 net6.0-macos)
net7.0-tvos (后续版本的 net6.0-tvos)
net7.0-windows (后续版本的 net6.0-windows)

实践理解

如何选择版本

https://dotnet.microsoft.com/zh-cn/platform/dotnet-standard#versions

前面提到从.Net 5+开始,微软就把.Net Standard的版本停留在2.1不再更新了,但是保持向前兼容。

.Net实现 1.0 1.1 1.2 1.3 1.4 1.5 1.6 2.0 2.1
.NET 5.0+ 5.0+ 5.0+ 5.0+ 5.0+ 5.0+ 5.0+ 5.0+ 5.0+
.NET Core 1.0+ 1.0+ 1.0+ 1.0+ 1.0+ 1.0+ 1.0+ 2.0+ 3.0+
.NET Framework 4.5+ 4.5+ 4.5.1+ 4.6+ 4.6.1+ 4.6.1+ 4.6.1+ 4.6.1+ N/A
Mono 4.6+ 4.6+ 4.6+ 4.6+ 4.6+ 4.6+ 4.6+ 5.4+ 6.4+
Xamarin.iOS 10.0+ 10.0+ 10.0+ 10.0+ 10.0+ 10.0+ 10.0+ 10.14+ 12.16+
Xamarin.Mac 3.0+ 3.0+ 3.0+ 3.0+ 3.0+ 3.0+ 3.0+ 3.8+ 5.16+
Xamarin.Android 7.0+ 7.0+ 7.0+ 7.0+ 7.0+ 7.0+ 7.0+ 8.0+ 10.0+
UWP 8.0+ 8.0+ 8.1+ 10.0+ 10.0+ 10.0.16299+ 10.0.16299+ 10.0.16299+ 待定
Unity 2018.1+ 2018.1+ 2018.1+ 2018.1+ 2018.1+ 2018.1+ 2018.1+ 2018.1+ 2021.2+
  • 如果你使用.Net Standard同时兼顾.Net Framework 4.6.1+和.Net Core,那么还是推荐你使用2.0版本较好,因为2.1不准备实现了。
  • 如果你使用.Net Standard同时兼顾UWP 10.0.16299+和.Net Core,那么还是推荐你使用2.0版本较好,因为2.1还没实现。

创建.Net Standard类库

打开Visual Studio创建新项目,筛选分类,可以看到一个就是类库

image

参考

posted @ 2022-08-29 19:59  TaylorShi  阅读(952)  评论(0编辑  收藏  举报