做为 .NET 系列的最新成员,.NET Core 和 .NET Standard 的概念及其与 .NET Framework 的区别并不十分明确。在本文中,我将准确介绍每一个产品及其适用场景。 web
在详细介绍以前,建议先审视一下 .NET 的整体状况,了解 .NET Core 和 .NET Standard 是如何在这一体系中发挥做用的。.NET Framework 是在 15 年前首次推出(我怎么记得2008年时我就在用.net framework1.1了?),当时只有一个 .NET 堆栈可用于生成 Windows 桌面和 Web 应用程序。从那之后,出现了其余 .NET 实现。例如,可用于生成 iOS/Android 移动应用和 macOS 桌面应用程序的 Xamarin,如图 1 所示。 后端
图 1:.NET 整体状况 浏览器
下面说明了 .NET Core 和 .NET Standard 是如何在这一体系中发挥做用的: 服务器
.NET Core 简介 网络
做为 .NET Framework 和 Silverlight 分支,.NET Core 是全新的 .NET 实现,不只跨平台,并且还彻底开放源代码。经过启用独立式 XCOPY 部署,它已通过优化,更适用于移动和服务器工做负载。 app
为了可以更好地认识 .NET Core,接下来将深刻了解如何进行 .NET Core 开发。为此,将同时探索新的命令行工具。也可使用 Visual Studio 2017 进行 .NET Core 开发,但本文读者极可能已至关熟悉 Visual Studio,所以我将重点介绍新体验。 框架
在 .NET 建立之初,它通过了大量优化,以便用户可以在 Windows 上快速开发应用程序。实际上,这意味着 .NET 开发与 Visual Studio 是不可分开的。能够确定的是:使用 Visual Studio 进行开发是一种奇妙的感觉。这样开发的工做效率超高,调试程序是我使用过的最好用的。 async
不过,有时,使用 Visual Studio 并非最便捷的选择。假设只是想经过 .NET 学习 C#。在这种状况下,就无需下载并安装好几个 GB 的 IDE。或者,假设要经过 SSH 访问 Linux 计算机。在这种状况下,使用 IDE 根本就行不通。又或者,假设只是喜欢使用命令行接口 (CLI)。 工具
正由于此,名为 .NET Core CLI 的一流 CLI 诞生了。.NET Core CLI 的主驱动程序称为"dotnet"。 它可用于开发的几乎全部方面,包括建立、生成、测试和打包项目。接下来,将了解具体操做。 学习
首先,建立并运行 Hello World 控制台应用程序(我使用的是 Windows PowerShell,而在 macOS 或 Linux 上使用 Bash 也一样有效):(命令行下建立项目、编译,对我这样的老年选手来除了装逼应该没什么别的用处了,固然我也没心思记住它们)
$ dotnet new console -o hello
$ cd hello
$ dotnet run
Hello World!
CLI 中的"dotnet new"命令至关于 Visual Studio 中的"文件 | 新建项目"。能够建立各类不一样类型的项目。键入"dotnet new"能够查看预安装的各类模板。
如今,将一些逻辑提取到类库中。为此,请先建立与"hello"项目平行的类库项目:
$ cd ..
$ dotnet new library -o logic
$ cd logic
因为要封装的逻辑是构造 Hello World 消息,所以将 Class1.cs 的内容更改成如下代码:
namespace logic
{
publicstaticclass HelloWorld
{
publicstaticstring GetMessage(string name) => $"Hello {name}!";
}
}
此时,还应将 Class1.cs 重命名为 HelloWorld.cs:
$ mv Class1.cs HelloWorld.cs
请注意,无需由于有此更改而更新项目文件。.NET Core 中使用的新项目文件只包括项目目录中的全部源文件。所以,添加、删除和重命名文件再也不须要修改项目。这样一来,能够更顺畅地使用命令行执行文件操做。
若要使用 Hello World 类,须要将 hello 应用程序更新为引用逻辑库。为此,能够编辑项目文件,也可使用 dotnet add reference 命令:
$ cd ../hello
$ dotnet add reference ../logic/logic.csproj
如今,将 Program.cs 文件更新为使用 HelloWorld 类,如图 2 所示。
图 2:将 Program.cs 文件更新为使用 HelloWorld 类
using System;
using logic;
namespace hello
{
class Program
{
staticvoid Main(string[] args)
{
Console.Write("What's your name: ");
var name = Console.ReadLine();
var message = HelloWorld.GetMessage(name);
Console.WriteLine(message);
}
}
}
若要生成并运行应用程序,只需键入 dotnet run:
$ dotnet run
What's your name: Immo
Hello Immo!
还能够经过命令行建立测试。CLI 支持 MSTest 和经常使用的 xUnit 框架。此示例使用 xUnit:
$ cd ..
$ dotnet new xunit -o tests
$ cd tests
$ dotnet add reference ../logic/logic.csproj
更改 UnitTest1.cs 内容以添加测试,如图 3 所示。
图 3:更改 UnitTest1.cs 内容以添加测试
using System;
using Xunit;
using logic;
namespace tests
{
publicclass UnitTest1
{
[Fact]
publicvoid Test1()
{
var expectedMessage = "Hello Immo!";
var actualMessage = HelloWorld.GetMessage("Immo");
Assert.Equal(expectedMessage, actualMessage);
}
}
}
如今,能够调用 dotnet test 运行测试:
$ dotnet test
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.
为了让操做变得更有趣一点,将建立简单的 ASP.NET Core 网站:
$ cd ..
$ dotnet new web -o web
$ cd web
$ dotnet add reference ../logic/logic.csproj
编辑 Startup.cs 文件,并将 app.Run 调用更改成使用 HelloWorld 类,以下所示:
app.Run(async (context) =>
{
var name = Environment.UserName;
var message = logic.HelloWorld.GetMessage(name);
await context.Response.WriteAsync(message);
});
若要启动开发 Web 服务器,只需再次运行 dotnet run:
$ dotnet run
Hosting environment: Production
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
转到所显示的 URL(应为 http://localhost:5000)。
此时,项目结构应如图 4 所示。
图 4:建立的项目结构
$ tree /f
│
├───hello
│ hello.csproj
│ Program.cs
│
├───logic
│ HelloWorld.cs
│ logic.csproj
│
├───tests
│ tests.csproj
│ UnitTest1.cs
│
└───web
Program.cs
Startup.cs
web.csproj
为了可以更方便地使用 Visual Studio 编辑文件,还将建立解决方案文件,并将全部项目添加到解决方案中:
$ cd ..
$ dotnet new sln -n HelloWorld
$ ls -fi *.csproj -rec | % { dotnet sln add $_.FullName }
能够看到,.NET Core CLI 功能很是强大,可以为具备其余背景的开发者提供至关熟悉的精益体验。虽然此过程是将 dotnet 与 Windows PowerShell 结合使用,但若是是在 Linux 或 macOS 上,使用体验也会很是类似。
.NET Core 的另外一巨大优点在于,它支持独立式部署。可使用 Docker 对应用程序进行容器化处理,以便它可以有本身的 .NET Core 运行时副本。这样一来,能够在使用不一样版本 .NET Core 的同一台计算机上运行各类应用程序,而它们则互不干扰。因为 .NET Core 开放源代码,所以还能够添加每日版或甚至本身修改或生成的版本,其中可能包括本身所作的修改。不过,这并不在本文的讨论范围以内。
.NET Standard 简介
在开发者生成新式体验时,应用程序一般跨越多种外形规格,于是也就跨越多个 .NET 实现。今时今日,客户很是但愿能够在手机上使用 Web 应用程序,并但愿可以经过基于云的后端共享数据。使用笔记本电脑时,他们也但愿能够经过网站得到访问权限。对于本身的基础结构,极可能但愿能够借助命令行工具和潜在的桌面应用程序,容许员工管理系统。请参阅图 5,了解具备此类用途的不一样 .NET 实现。
图 5:介绍了 .NET 实现
|
OS |
开源 |
用途 |
.NET Framework |
Windows |
N |
用于生成在 IIS 上运行的 Windows 桌面应用程序和 ASP.NET Web 应用程序。 |
.NET Core |
Windows、Linux、macOS |
Y |
用于生成跨平台的控制台应用程序、ASP.NET Core Web 应用程序和云服务。 |
Xamarin |
iOS、Android、macOS |
Y |
用于生成适用于 iOS 和 Android 的移动应用,以及适用于 macOS 的桌面应用程序。 |
.NET Standard |
无 |
Y |
用于生成能够从全部 .NET 实现(如 .NET Framework、.NET Core 和 Xamarin)引用的库。 |
在这样的环境下,代码共享成为重大挑战。须要了解 API 的可用位置,并确保共享组件仅使用跨全部要用 .NET 实现都支持的 API。
此时,.NET Standard 就派上用场了。.Net Standard 是一种规范。每一个 .NET Standard 版本都定义了一组 API。为了与相应版本保持一致,全部 .NET 实现都必须提供这些 API。能够将 .NET Standard 看做是另外一个 .NET 堆栈,不一样之处在于没法为其生成应用程序,只能生成库。若但愿能够从任意位置引用库,应对库使用此 .NET 实现。
你们不由想知道,.NET Standard 涵盖了哪些 API。若是熟悉 .NET Framework,就应该熟悉我以前提到的 BCL。BCL 是与 UI 框架和应用程序模型无关的一组基础 API。它包括基元类型、文件 I/O、网络、反射、序列化、XML 等。
全部 .NET 堆栈都会实现某版 .NET Standard。经验法则是,生成新版 .NET 实现时,一般都会实现最新版 .NET Standard。
很贴切的例子是 HTML 和浏览器:将 HTML 规范看做是 .NET Standard,将不一样的浏览器看做是 .NET 实现,如 .NET Framework、.NET Core 和 Xamarin。
此时,你们可能很是好奇,如何使用 .NET Standard。实际上,你们已经知道了。还记得以前建立逻辑类库时的情形吗? 接下来,将深刻了解一下项目文件:
$ cd logic
$ cat logic.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
将此文件与"hello"控制台应用程序项目文件进行对比:
$ cd ..\hello
$ cat hello.csproj
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\logic\logic.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
</Project>
能够看到,逻辑库的 TargetFramework 值为 netstandard2.0,而控制台应用程序的值为 netcoreapp2.0。TargetFramework 属性指明要定目标到的 .NET 实现。所以,控制台应用程序定目标到 .NET Core 2.0,而库定目标到 .NET Standard 2.0。也就是说,不只能够从 .NET Core 应用程序引用逻辑库,还能够从生成的 .NET Framework 或 Xamarin 应用程序引用逻辑库。
遗憾的是,目前可用的大多数库都还没有定目标到 .NET Standard。大多数库都定目标到 .NET Framework。固然,并非全部库均可以(或甚至应该)定目标到 .NET Standard。例如,包含 Windows Presentation Foundation (WPF) 控件的库须要定目标到 .NET Framework,由于 UI 并非标准的一部分。不过,许多常规用途库定目标到 .NET Framework 只是由于,在它们建立时尚未 .NET Standard。
在 .NET Standard 2.0 推出后,API 集变得很是大,大多数(若是不是所有的话)常规用途库能够定目标到 .NET Standard。所以,目前 NuGet 上的所有库中有 70% 只使用如今属于 .NET Standard 的 API。尽管如此,其中只有一小部分明确标注为与 .NET Standard 兼容。
为了让开发者无障碍地使用它们,添加了一种兼容性模式。若是安装的 NuGet 包没有为目标框架提供库,也没有为 .NET Standard 提供库,那么 NuGet 会转而求助于 .NET Framework。也就是说,能够引用 .NET Framework 库,就像是定目标到 .NET Standard 同样。
我将演示一下具体操做。在此示例中,我将使用名为 PowerCollections 的经常使用集合库,它是在 2007 年编写的。它有一段时间没有更新了,仍定目标到 .NET Framework 2.0。我将从 NuGet 将此库安装到 hello 应用程序中:
$ dotnet add package Huitian.PowerCollections
此库提供了 BCL 没有的其余集合类型,如不保证排序的包。接下来,将把 hello 应用程序更改成使用此库,如图 6 所示。
图 6:使用 PowerCollections 的示例应用程序
using System;
using Wintellect.PowerCollections;
namespace hello
{
class Program
{
staticvoid Main(string[] args)
{
var data = new Bag<int>() { 1, 2, 3 };
foreach (var element in data)
Console.WriteLine(element);
}
}
}
若是运行程序,将会看到如下警告:
$ dotnet run
hello.csproj : warning NU1701: Package 'Huitian.PowerCollections 1.0.0' was restored using'.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.0'. This may cause compatibility problems.
1
3
2
那么,究竟发生了什么? hello 应用程序定目标到 .NET Core 2.0。因为 .NET Core 2.0 实现的是 .NET Standard 2.0,所以还有可引用 .NET Framework 库的兼容性模式。不过,并非全部 .NET Framework 库都适用于各类 .NET 实现。例如,它们可能会使用 Windows 窗体或 WPF API。因为没法肯定具体状况,所以 NuGet 会显示警告消息,提醒注意这种状况,这样就不会浪费时间排查可能由此致使的问题了。
请注意,每次生成时都会看到此警告。这样可避免在包安装期间根本看不到警告或忘记这种状况的问题。
固然,没有什么比每次生成时都须要忽略没法做为行动依据的警告更糟糕的了。所以,建议在验证应用程序后,为相应包禁用警告。因为应用程序运行正常(正确输出了所建立的包的内容),所以如今能够取消警告。为此,请编辑 hello.csproj 文件,并将 NoWarn 属性添加到包引用中:
<PackageReference Include="Huitian.PowerCollections" Version="1.0.0"
NoWarn="NU1701" />
若是如今从新运行应用程序,就不再会看到警告了。若是安装另外一个使用兼容性模式的包,也会看到能够取消的相应包警告。
使用新工具,还可让类库项目建立属于生成的 NuGet 包。这样一来,能够更轻松地与全世界共享库(经过推送到 nuget.org),也能够仅在组织内共享(经过推送到你们在 Visual Studio Team Services 或 MyGet 上本身的包源)。新项目还支持多重定目标,这样就能够生成一个适用于多个 .NET 实现的项目。也就是说,可使用条件编译 (#if) 来调整库,使其适应特定的 .NET 实现。还能够生成平台专属 API 的 .NET Standard 包装器。不过,这些都不在本文的讨论范围以内。
结束语
.NET Standard 是全部 .NET 实现都必须提供的 API 规范。这样一来,可让 .NET 系列保持一致,并生成可以从任何 .NET 实现引用的库。它取代了生成共享组件的 PCL。
.NET Core 是更适合使用 ASP.NET Core 生成控制台应用程序、Web 应用程序和云服务的 .NET Standard 实现。它的 SDK 随附功能很是强大的工具,除了支持 Visual Studio 开发外,还支持基于命令行的完整开发工做流。