我假设您已经阅读了微软的文章,并且通过其中的表格一切都变得清晰易懂。如果是这样,那么您和我之前在花费大部分下午研究此问题(并尝试将我的反射库移植到.NET Core,这也是我唯一的.NET Core移植工作)之前处于同一条船上。以下不是官方立场,而是我通过大量阅读以及作为.NET 1.0开发人员所发现的个人总结。我无法保证其完全准确(特别是涉及移动开发方面,我几乎一无所知),欢迎指正。如果有很多错误,我会将其变成维基百科。
我将按照时间顺序逐步介绍,因为我发现这样最有意义,如果您想了解旧版和新版之间的关系。它可能需要大幅简化,但目前我没有时间这样做。然而,在结尾处确实有一个TL;DR版本。
漫长而曲折的道路
尽管.NET起源于Java,但它从未真正试图成为“编写一次,随处运行”。它最初非常在Windows阵营中,并且即使它编译成字节码并没有过度使用显式的Windows操作系统相关代码,因此理论上非常易移植,但这并不是微软真正感兴趣的。.NET Framework的一部分早期开源,一群开源爱好者接手并运用它,给我们带来了Mono。Mono很重要,因为它是.NET的第一个替代平台和库集,并且说明了平台与库集与工具链的思想。Mono试图提供(更或多或少)完整的公共语言运行时及其关联的基础类库实现。这很重要:尽管Mono在Linux(和其他一些Unix系统)上运行,但它不是一个单独的平台,因为它实现了(某些版本的)CLR + BCL。对于应用程序开发人员而言,存在运行时差异(路径名等),但对于实际的库编程,您可以将Mono和Windows的.NET Framework视为具有稍微不同实现的“相同”平台。我强调这一点,因为我们将遇到针对Windows的.NET代码,该代码没有在CLR上运行,因此更难进行移植(无论是否具有讽刺意味)。
接着出现了Windows Phone(多个版本),Windows CE(多个版本),Windows Embedded CE,Windows Toaster(好吧,这个并不存在),每次基本上都会重新发明一些 .NET 的味道,但是运行时和/或 BCL 中的基础内容会缺失或更改。这就是我们得到.NET Compact、.NET Micro、Windows Phone(旧版,没有单独的框架名称)和Silverlight的地方。所有这些都应该被视为类似于 .NET Framework 的独立平台 + 库组合,从而使跨平台开发成为可能,但与它不同的是,使其不那么容易。由于跟踪支持的位置变得最不方便,因此有人想出了 可移植类库 以及它们的相关配置文件(它们的集合称为.NET Portable 参考程序集)。
基本上,您需要针对一组特定的.NET版本、平台(和库)进行目标设置,然后您将获得模仿这些组合的参考程序集以进行编译。根据您明确希望针对哪些风味,存在许多不同的配置文件。这是 .NET Standard 现在试图在更大范围内尝试的第一次尝试。基本上,
除非您针对 .NET Standard 不支持的内容,否则 PCL 已经过时。.NET Standard 摆脱了无数不同配置文件的想法,这很好,但代价是削减了您的库可以较早地针对的一些内容,这是不好的。有在线资源可帮助从 PCL 过渡到 .NET Standard。如果您现在正在查看可移植代码,则不要专注于 PCL,除非您真的想支持一些相当边缘的平台(不打算冒犯仍在为它们开发的人)。
通用 Windows 平台,顾名思义,是一个平台。具体来说,它是由 Windows 应用商店(包括桌面和手机应用)支持的 .NET 平台。仅此而已,没有更多,也没有更少。最好视为 Silverlight 的自然继承者,因为它是一个沙盒化框架,支持桌面和移动设备。尽管名字叫做“通用”,但它并不是你想要让所有代码都针对的平台。它是一个你可能希望为你的代码启用的平台,并且独特之处在于它是我所知道的唯一一个具有相同版本中两个运行时的平台。马上就来!
.NET Native在原帖中没有提到,但在这些讨论中经常出现,因为它和.NET Core一样是新的,并且听起来非常性感,因为它可以直接将.NET编译成机器代码(预先编译,而不是JIT编译)。它不是一个完全新的平台,而是用于UWP应用程序的新运行时(仅限于这些应用程序在发布模式下编译时使用)。在调试模式下,它们使用CoreCLR(即.NET Core运行时)。除非你真的想构建一个UWP应用程序,否则你不需要过多考虑这个问题,因为在.NET Native中,反射方面有很多有趣的事情需要开发人员单独关注。
现在我们来谈一下.NET Core! .NET Core最初是作为“ASP.NET Core”开始的,但人们很快意识到它可以比那更大。.NET Core是一个新的运行时(CoreCLR)+库组合,具有明确的跨平台支持(即跨操作系统)。与CLR + BCL组合不同,其中Windows版本和Unix版本以Mono的形式存在,.NET Core是所有平台的一个代码库(当然,还有通常的特定于平台的细节以支持蓬松的可移植层上方)。进一步让人困惑的是,.NET Core也是一种新的工具链/项目类型,用于支持构建.NET Core应用程序,在此之前我们只有MSBuild。这是因为Linux没有Visual Studio,所以必须这样做,但微软已经在摆脱这种“让它简单且使用JSON”的方法,转而回归到一个通用格式同时支持.NET Framework和.NET Core(它将是MSBuild,因为有更多的依赖于它)。
.NET Core在很大程度上与.NET Framework兼容。因此,如果您的.NET Core应用程序实际上在.NET Framework(在Windows上)上运行,它可以加载针对.NET Framework而不仅仅是.NET Core的程序集。这是混淆和不可移植代码的重要原因:虽然您可以构建并加载这些程序集,但它们会使您的代码不可移植。.NET Core本身不会阻止您这样做;.NET Standard(即将推出)将在正确对齐声明的情况下才能阻止。
跟上了吗?很好,因为现在我们准备向你介绍.NET Standard。.NET Standard不是一个平台(不能下载安装到机器上),也不是一个库(但有支持它的包用于构建目的),它是一个档案。它试图标准化刚才讨论过的各种事物的库表面。其想法是,如果您的代码针对.NET Standard X.Y,您只需要将构建工具切换到“请给我.NET Standard X.Y”,并构建您的程序集时,您可以确信它将可用于由X.Y覆盖的所有平台。太棒了!世界又简单了!
“现在还不是这样。问题在于,.NET Core目前正在进行大量开发,这意味着很多东西都缺失或不同——甚至是非常基础的东西,在您的.NET Framework代码库中可能自然存在,比如将异常标记为
Serializable
并为其提供一个单独的构造函数以进行反序列化,以便它们可以在
AppDomain
之间良好地工作。.NET Core中没有
AppDomain
,因此没有序列化,因此这些构造函数将无法编译。即使
Serializable
属性本身在旧版本的CoreCLR中也不可用。如果您的MSBuild项目使用自定义目标,那就太糟糕了,.NET Core工具链目前不支持该功能(当它再次成为MSBuild时,可能会有支持)。当然,您可以重写,但无法重用。因此,虽然您可以针对.NET Standard,但您可能需要两个单独的项目和/或一些条件编译来获取适用于两个不同平台的.NET Standard程序集。如果你幸运(或可以妥协一点),你的库足够简单,只使用.NET Core构建就足够了。不要误解:仍然存在多个.NET平台,它们仍然有差异,.NET Standard只是试图使移植更加容易。到目前为止,它的功能有限,但已经比PCL做得更好了。”
简而言之:.NET Core和.NET Framework(以及它们的所有小表亲)在概念上和实现上都是不同的平台。.NET Standard是一种目标框架,可以简化在它们之间移植代码所需的工作(但还不能完全透明)。PCL是Standard的前身,如果你是进步的话可以忽略它。
TL;DR从这里开始(但仍然很长)
最终回答你的问题,如果你是一名现代图书馆开发人员,并且想要面向“尽可能多的受众”,那么你应该做什么呢?首先,如果可能的话,你必须缩小目标范围。你会明确支持和测试哪些平台?你真的想在XBox 360上针对.NET Compact、Windows Phone 7或八年前的Silverlight吗?如果是这样,你可能无法避免PCL,但我们大多数人都有幸能够避免它。如果不行:如果你的配置文件与.NET Standard中的任何内容都不匹配,请排队等待单独的PCL构建。
如果你的库非常简单(不使用反射、不使用
async
/
await
,不依赖于像WCF这样的大型框架),那么你可能能够针对.NET 2.0和具有所需框架依赖关系的最低版本的.NET Standard。不要被愚弄,较低版本的.NET Standard在提供的功能方面真的令人失望,但是你必须为可移植性付出一些代价。没有工具链支持同时构建.NET 2.0和某个.NET Standard版本。你必须构建两次并在“每个地方”进行测试,尽管交叉部分意味着如果编译通过,你可以相当肯定它会正常工作。生成的库将支持几乎所有可以加载纯.NET 2.0程序集的.NET平台(几乎所有平台,但特别不包括Micro和Compact)和.NET Core,而且无需平台切换。恭喜,世界上从未见过如此便携的东西!
如果你的库使用了反射,那么你可能无法避免重写代码以使其能够在.NET Core中编译,因为
反射API已经改变,而你的代码可能还没有跟上(因为你一直只针对完整框架)。在这种情况下,你需要将目标.NET Framework版本提升到4.5,因为这是与这些更改兼容的.NET Framework版本。在这里你开始得到工具支持:你可以针对.NET Standard 1.1,它覆盖了.NET 4.5的
子集。如果你发现这个子集不够用,你就必须再次构建两次:一次是针对完整的.NET Framework,另一次是针对.NET Core。原因是.NET Core 1.0支持比.NET Framework 4.5更多的内容,但是目前还没有与Core相媲美的.NET Framework版本(那将是“vNext”)。因此,如果你不想仅限于.NET Core,而且还想支持我们仍在构建纯粹的4.5桌面应用程序,而.NET Standard 1.1对你来说不够用,那么你就必须进行拆分。错误的做法是针对1.1,并导入仅支持Framework 4.5的包/程序集,因为这将在可移植性方面带来两个世界中最糟糕的结果!
如果您的库需要一些在4.5.1或更高版本中引入的改进/扩展,或者只有在更高的.NET Standard版本中才可用的包,则应选择相应的更高.NET Standard版本。请注意,Microsoft
不再官方支持任何低于4.5.2的4.x版本。这并不意味着您不应该针对那些版本(尽可能低),但这确实意味着您有理由使用不低于.NET Standard 1.2,如果您可以要求4.6,则不低于1.5。这对消费者来说并不繁琐(如果您愿意且能够安装4.6,则几乎肯定愿意且能够安装4.6.2),并使您的生活更轻松。根据您的代码,您可以仅使用.NET Core构建,但您可能不想这样做,因为.NET Core构建链尚不稳定,并将返回到MSBuild(如前所述)。没有必要放弃所有项目文件以仅使用JSON,以后再次移回!
你的库是否使用了任何条件编译?请注意,在.NET Core工具链中,你会得到不同的预定义符号。它们非常烦人,因为它们坚持(比如)区分4.5、4.5.1和4.5.2之间的差异,如果你想覆盖“4.5及以上”的话,这是一件很麻烦的事情。虽然仔细的构建可以解决这个问题,但你仍然需要考虑这个问题。
我没有涉及移动构建(Xamarin和旧版Phone)的内容,因为我对此知之甚少!我想,构建方法与同时构建.NET Core和.NET Framework的方法大致相同,即只有在简单的库和不需要考虑向后兼容性的库中才能够进行一次构建,否则需要(至少)两次构建。但正如我在一开始所说的,欢迎您提出更正意见。
async
/await
进行单独支持,请使用.NET 4.5 Framework。.NET Compact已经死亡,.NET Micro快要死亡,Core取代了PCL(= .NET Portable),UWP是Windows Store的.NET,.NET Standard试图统一.NET Core和.NET Framework,但仍有很长的路要走才能不让人感到困惑。这是我的个人TL;DR,但我必须说,您为一个好答案提供300分是完全正确的。 - Jeroen Mostert