我假设您已经阅读了带有漂亮桌子的 Microsoft 文章,现在一切都一清二楚了。如果是这样,那么在我花了一个下午的大部分时间研究这个之前,你和我在同一条船上(并试图将我的反射密集型库移植到 .NET Core,我应该提到这也是我的 仅 .NET Core 移植工作)。以下不是官方的派对路线,而是我通过大量阅读(以及自 .NET 1.0 以来成为 .NET 开发人员)所发现的内容的个人总结。我不能保证它的完全准确性(尤其是在移动开发方面,我几乎完全不知道),当然欢迎更正。如果有很多,我会做维基。
我将或多或少地按时间顺序进行介绍,因为我发现如果您想了解新旧之间的关系,这才是最有意义的。它可能应该减少很多,但目前我没有时间这样做。不过,有一个 TL;DR 版本在最后。
漫长而曲折的路
尽管有 Java 血统,.NET 从未认真尝试过“一次编写,随处运行”。它最初是在 Windows 阵营中开始的,尽管它编译为字节码并且没有过度使用显式的 Windows 主义,因此理论上非常便携,但这并不是 MS 真正感兴趣的。 .NET Framework 的一部分很早就是开源的,一群开源爱好者选择并使用它,为我们提供了 Mono。 Mono 很重要,因为它是 .NET 的第一个备用平台和库集,并说明了平台、库和工具链的概念。 Mono 尝试提供(或多或少)公共语言运行时及其相关基类库的完整实现。这很重要:尽管 Mono 在 Linux(和其他一些 Unices)上运行,但它不是一个单独的平台,因为它实现了(某些版本的)CLR + BCL。对于应用程序开发人员来说,存在运行时差异(路径名等),但对于实际的库编程,您可以考虑 Mono 和 .NET Framework for Windows“相同”平台,但实现略有不同。我强调这一点是因为我们将遇到针对 Windows 的 .NET 代码,这些代码不在 CLR 上运行,并且(具有讽刺意味或其他方面)更难移植。
然后出现了 Windows Phone(多个版本)、Windows CE(多个版本)、Windows Embedded CE、Windows Toaster(好吧,那并不真正存在)和一些 .NET 风格基本上每次都被重新发明——仍然是 .NET,但运行时和/或 BCL 的基本内容丢失或更改。这是我们获得 .NET Compact、.NET Micro、Windows Phone(旧风格,没有单独的框架名称)和 的地方银光。所有这些都应该被视为独立的平台 + 库组合,它们与 .NET 框架非常相似,足以使跨平台开发成为可能,但与它的差异又足以使其没那么容易。为了跟踪对共享库来说最不方便的地方支持什么,有人提出了可移植类库及其相关配置文件(其集合被称为 .NET便携式参考组件)。
基本上,您以 .NET 版本、平台(和库)的一组特定组合为目标,并且您将获得模仿这些组合进行编译的参考程序集。根据您明确希望定位的口味,存在许多不同的配置文件。这是 .NET Standard 现在大规模尝试的第一次尝试。基本上,PCL 现在已经过时,除非您的目标是 .NET Standard 不支持的东西。 .NET Standard 摒弃了使用大量不同配置文件的想法,这很好,但代价是削减了您的库可能更早针对的一些内容,这很糟糕。有在线资源可帮助您从 PCL 过渡到 .NET Standard。如果您现在正在研究可移植代码,那么您不想专注于 PCL,除非您真的想支持一些非常边缘的平台(无意冒犯仍在为它们开发的人)。 p>
通用 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 版本和一个 Mono 形式的 Unix 版本,.NET Core 是一个适用于所有平台的代码库(当然,具有特定于平台的松脆位以支持上面的蓬松可移植层) .更让人困惑的是,.NET Core 也是一个新的工具链/项目类型的名称,用于支持构建 .NET Core 应用程序,而在此之前我们只有 MSBuild。这是因为没有适用于 Linux 的 Visual Studio,但 MS 是 already moving away,来自这种“让我们保持简单和 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 XY 为目标,您只需将构建工具切换为“请给我 .NET Standard XY”,并且当您构建程序集时,您可以确定将可用于 XY 涵盖的所有平台万岁!世界又简单了!
嗯,还不是。问题是 .NET Core 目前正处于繁重的开发中,这意味着很多东西缺失或不同——甚至是非常基本的东西,它们可能自然存在于你的 .NET Framework 代码库中,比如标记你的异常Serializable 和为它们提供一个单独的反序列化构造函数,以便它们在AppDomains 中很好地工作。 .NET Core 中没有 AppDomains,因此没有序列化,因此这些构造函数不会编译。即使是 Serializable 属性本身也在旧版本的 CoreCLR 中丢失。如果您的 MSBuild 项目使用自定义目标,那太糟糕了,.NET Core 工具链目前不支持这些东西(它可能在将来再次成为 MSBuild 时)。当然,您可以重写,但不能重用。因此,虽然您可以以 .NET Standard 为目标,但您可能需要两个单独的项目和/或一些条件编译来获得适用于两个不同平台的 .NET Standard 程序集。如果你很幸运(或者可以妥协一点),你的库足够简单,仅使用 .NET Core 构建它就足够了。但请不要误会:仍然有多个 .NET 平台,它们仍然存在差异,.NET Standard 只是试图使其更容易移植。到目前为止,它是有限的,但已经比 PCL 做得更干净了。
总结一下:.NET Core 和 .NET Framework(以及它们的所有小表亲和侄子)在概念上和实现上都是独立的平台。 .NET Standard 是一种目标配置文件,可简化在它们之间移植代码所需的工作(但尚未使其完全透明)。 PCL 是标准的前身,如果你是进步主义者,可以忽略它。
TL;DR 从这里开始(但仍然是 TL)
为了最终回答您的问题,如果您是一名现代图书馆开发人员,并且您想针对“尽可能多的受众”,您应该怎么做?首先,如果可能,您必须将其缩小。您将明确支持和测试哪些平台?您真的想在您的 Xbox 360 上以 .NET Compact 为目标吗?视窗电话 7?八年前的银光?如果这样做,您可能无法绕过 PCL,但我们大多数人都可以避免这种情况。如果不是:如果您的配置文件与 .NET Standard 中的任何内容都不匹配,则排队单独构建 PCL。
您的库是否非常简单(不使用反射,不使用 async/await,不依赖于 WCF 等更大的框架)?然后,您可能能够针对具有所需框架依赖项的 .NET 2.0 和 最低 版本的 .NET Standard 的横截面。不要被愚弄,.NET Standard 的低版本在提供的功能上确实令人失望,但您必须为可移植性付出一些代价。没有为 .NET 2.0 和某些 .NET Standard 版本构建的工具链支持。您必须构建它两次并在“任何地方”对其进行测试,尽管横截面意味着您可以合理地确定它在编译时会起作用。生成的库将支持每个可以加载 vanilla .NET 2.0 程序集(几乎所有程序集,但尤其不是 Micro 和 Compact)和 .NET Core 的单个 .NET 平台,并且所有平台都没有平台切换。恭喜,世界从未见过如此便携的东西!
您的库会使用反射吗?那么您可能无法绕过重写代码以使其为 .NET Core 编译,因为 reflection API changed a while back 和您的代码可能还没有赶上(因为如果您只针对完整框架,则没有必要)。在这种情况下,您需要将目标 .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 或更高版本中引入的超过 4.5 的某些改进/扩展,或者仅适用于更高 .NET Standard 版本的包?然后改为以适当的更高 .NET Standard 版本为目标。请注意,微软no longer officially supports any 4.x lower than 4.5.2。这确实不意味着您不应该针对那些版本(尽可能低),但它确实意味着您有理由使用不低于 . NET Standard 1.2,如果您可以要求 4.6,则不低于 1.5。这对消费者来说不是负担(如果您愿意并且能够安装 4.6,那么您几乎可以肯定愿意并且能够安装 4.6.2)并使您的生活更轻松。根据您的代码,您可以只使用 .NET Core 构建,但您可能不想这样做,因为 .NET Core 构建链还不稳定,将移回 MSBuild(如前所述)。放弃所有项目文件以使用 JSON 只是为了稍后再返回是没有意义的!
您的库是否使用任何类型的条件编译?请注意,使用 .NET Core 工具链,您会得到 different predefined symbols。他们特别烦人,因为他们坚持(比如说)区分 4.5、4.5.1 和 4.5.2,如果你想涵盖“4.5 及更高版本”,这会很痛苦。没有什么是精心构建无法处理的,但您需要考虑一些事情。
我不会在这里介绍移动版本(Xamarin 和旧手机版本),因为我对这些知之甚少!我想这个故事与为 .NET Core 和 .NET Framework 构建非常相似,因为该构建曾经只适用于简单的库和您不必关心向后兼容性和需求的库(至少)否则有两个构建,但正如我在一开始所说的那样,欢迎更正。