三、创建库

可以通过创建库来使用共享代码。在Visual Studio 2017中,有许多创建库的选项,如下所示:

  • Class Library (.NET Core)
  • Class Library (.NET Standard)
  • Class Library (.NET Framework)
  • WPF Custom Control Library (.NET Framework)
  • WPF User Control Library (.NET Framework)
  • Windows Froms Control Library (.NET Framework)
  • Class Library (Universal Windows)
  • Class Library (Legacy Protale)
  • Shared Project

上面列出的Shared Project并不是一个真正的类库,但是可以使用它来共享多个项目中的代码。

在前面的列表中,使用.NET Framework标识的类库应该与.NET Framework一起使用,它们可以有特定的限制。WPF User Control Library和WPF Custom Control Library只能在WPF应用程序中使用。类似地,Windows Froms Control Library只能在Windows Forms应用程序中使用。

Class Library(Legacy Portable)已经在名字中包含Legacy了。这个项目模版最初称为Portable Class Library(PCL),不应该用于新的应用程序。这个库能够共享不同技术之间的代码,例如在Silverlight、WPF、Xamarin、.NET Core等之间共享代码。根据平台和版本的选择,可以使用不同的API。平台越多,选择的版本越老,可用的API就越少。随着添加的平台越来越多,就增加了定义的复杂性,也增加了在可移植的库中使用可移植库的复杂性。

.NET 标准为可移植的库提供了替代品。

.NET 标准

.NET 标准对可用的API进行了线型定义,这与可用于可移植库的API的矩阵定义不同。.NET 标准的每个版本都添加了API,而API从未删除。

.NET 标准的版本越高,可以使用的API越多。然而,.NET 标准并没有实现API;它只是定义了需要由.NET平台实现的API。这可以与接口和具体类相比较。接口为需要由类实现的成员定义了协议。在.NET 标准中,.NET标准指定了哪些API需要可用,以及需要实现这些API的.NET平台——支持特定版本的标准。

在https://github.com/dotnet/standard/tree/master/docs/versions中可以找到哪些API可用于哪个标准版本,以及标准之间的差异。

.NET 标准的每个版本都将API添加到标准库中:

  • .NET Standard 1.1在.NET Standard 1.0中添加了2414个API。
  • 版本1.2只添加了46个API。
  • 在1.3版本中,添加了3314个API。
  • 1.4版本只添加了18个加密API。
  • 1.5版本主要增强了反射支持,增加了242个API。
  • 1.6版本增加了更多的加密API和增强的正则表达式,共额外添加了146个API。

在.NET Standard 2.0中,微软进行了大量的投资,使其更容易将旧应用程序迁移到.NET Core。在这个新标准中,添加了19507个API。并非所有这些API都是新的。有些已经在.NET Framework 4.6.1中实现了。例如,像DataSet、DataTable之类的旧API现在可用于.NET标准。这是为了便于将旧应用程序迁移到.NET标准中。.NET Core需要大量的投资,因为.NET Core 2.0实现了.NET Standard 2.0。

哪些API不是标准的。永远都不会变成标准?特定于平台的API不可能成为.NET 标准的一部分。例如,Windows Presentation Foundation(WPF)和Windows Forms定义了特定于Windows的API,而这些API不会成为标准。但是,可以创建WPF和Windows Forms应用程序,并在其中使用.NET标准库。不能创建包含WPF或Windows Fomrs控件的.NET标准库。

测试过的新API将首先进入.NET Core。一旦API稳定下来,就可以用于.NET标准的未来版本。

下面讨论更多关于.NET标准平台支持的细节。如果需要支持Windows Phone Silverlight 8.1,就要将库限制为可用于.NET Standard 1.0中的API。当然,现在通常不需要支持任何Silverlight版本。下面介绍更重要的.NET 平台。

对于使用通用Windows平台的库,需要注意要支持的构建号。如果只支持Fall Creators Update of Windows 10,就可以使用.NET Standard 2.0。为了支持Creators Update和旧的Windows 10构建版本,可以升级到.NET Standard 1.4。在这种情况下,新版本是不可用的。如果创建一个ASP.NET Core 1.1控制器库,库就需要是标准的1.6版。

注:要支持尽可能多的平台,需要选择较低的.NET标准版本。要获得更多的API,请选择更高.NET标准版本。

创建.NET标准库

要创建.NET 标准库,可以通过如下命令使用.NET Core CLI工具。

dotnet new classlib

使用.NET Core 2.0 CLI工具,将创建一个具有这个csproj定义的库:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

</Project>

通过更改TargetFramework元素,可以更改.NET标准库的版本。在Visual Studio中,可以使用Project Properties的Application Settings设置来更改.NET标准版本。

解决方案文件

使用多个项目(例如,一个控制台应用程序和一个库)时,使用解决方案文件是很有帮助的。在.NET Core CLI工具的新版本中,可以在命令行中使用解决方案,也可以在Visual Studio中使用它们。例如,下面的命令

> dotnet new sln

在当前目录中创建一个解决方案文件。

使用dotnet sln add命令,可以向解决方案文件中添加项目:

dotnet sln add SimpleLib/SimpleLib.csproj

项目文件添加到解决方案中,如下面的代码片段所示:


Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lookup", "Lookup.csproj", "{FC8FC406-9F5D-4E73-B8A3-6F448C836855}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Debug|x64 = Debug|x64
		Debug|x86 = Debug|x86
		Release|Any CPU = Release|Any CPU
		Release|x64 = Release|x64
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Debug|x64.ActiveCfg = Debug|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Debug|x64.Build.0 = Debug|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Debug|x86.ActiveCfg = Debug|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Debug|x86.Build.0 = Debug|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Release|Any CPU.Build.0 = Release|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Release|x64.ActiveCfg = Release|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Release|x64.Build.0 = Release|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Release|x86.ActiveCfg = Release|Any CPU
		{FC8FC406-9F5D-4E73-B8A3-6F448C836855}.Release|x86.Build.0 = Release|Any CPU
	EndGlobalSection
EndGlobal

引用项目

使用dotnet add reference命令可以引用一个库。当前目录只需要定位在应该添加库的项目的目录中:

dotnet add reference ...\SimpleLib\SimpleLip.csproj

在csproj文件中使用Project Reference元素来添加引用:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\BitVector32\BitVector32.csproj" />
  </ItemGroup>

</Project>

在Visual Studio中使用Solution Explorer,可以选择Dependencies节点,然后从Project菜单中选择Add Reference命令,从而向其他项目添加项目。打开的对话框如下图所示。

引用NuGet包

如果库已经打包在Nuget包中,则可以直接使用命令dotent add package来引用NuGet包。

dotnet add package Microsoft.Composition

它没有像以前一样添加一个ProjectReference,而是添加了一个PackageReference。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Composition" Version="1.0.31" />
  </ItemGroup>

</Project>

为了请求包的特殊版本,可以使用.NET Core CLI命令指定-version选项。在Visual Studio中,可以使用Nuget包管理器(如下图)找到包,并选择包的一个特定版本。使用此工具,还可以获得项目的细节、与项目的链接和许可信息。

注意

www.nuget.org上,并不是所有的包都对应用程序有用。应该检查许可信息,以确保许可证符合项目需求。另外,应该检查包的作者。如果它是一个开源的包,那么它背后的社区有多活跃?

 NuGet的来源

包从哪里来?www.nuget.org是微软和第三方上传.NET包的服务器。首次从NuGet服务器上下载包之后,包就存储在用户配置文件中。因此,用相同的包创建另一个项目会快得多。

在Windows上,用户配置文件中包的目录是...\nuget\packages。也使用其他临时目录。要获取关于这些目录的所有信息,最好安装NuGet命令行实用程序,它可以从https://dist.nuget.org/下载。

要查看全局包、HTTP缓存和temp包的文件夹,可以使用nuget local:

> nuget locals all -list

在一些公司中,只允许使用经过批准并存储在本地NuGet服务器中的包。NuGet服务器的默认配置在C:\user\username\AppData\Roaming\Nuget目录的文件NuGet.Config中。

默认配置类似于下面的NuGet.Config文件。包从https://api.nuget.org和本地的NuGetFallbackFolder中加载。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
    <add key="CliFallbackFolder" value ="C:\Users\chris\.dotner\NuGetFallbackFolder"/>
  </packageSources>
</configuration>

可以通过添加和删除包源来更改默认值。

微软并没有在主NuGet服务器上的日常构建中存储包。为了使用.NET Core NuGet包的日常构建,需要配置其他NuGet服务器。为了使用.NET Core NuGet包的日常构建,需要配置其他NuGet服务器。还可以配置本地目录,在其中可以放置定制的NuGet包。以下Nuget.Config文件添加了一个本地目录和.NET Core包到包源的夜间提要。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="local packages" value="C:\git\mypackages"/>
    <add key="dotnet-core" value ="https://dotnet.muget.org/F/dotnet-core/api/v3/index.json"/>
  </packageSources>
</configuration>

可以将配置文件放置到项目目录中,而不是更改默认配置文件。这样,配置的包源只对项目有效。

使用.NET Framework库

如本章前面所述,.NET Standard 2.0的一大优点是来自.NET Framework的额外API。这便于将旧.NET应用程序迁移到新的.NET。

.NET标准库可以在许多不同的.NET技术中使用。当然,可以从.NET Core中引用.NET标准库,也可以从Mono和.NET Framework项目中引用。WPF应用程序可以使用.NET标准库。使用.NET Core 1.0创建的库已经可以实现这样的场景。但是现在,在.NET Standard 2.0中,扩展了互操作场景。只要.NET Framework库只使用可用于.NET标准的API,可以从.NET标准库中引用旧的.NET Framework库。

这怎么可能?尝试使用早期.NET Core版本的互操作场景常常会导致兼容性错误。例如,对象类型定义了两次。这些问题的原因很容易解释。使用老的.NET技术,例如.NET Framework,在mscrolib中定义了object类等核心类型。使用诸如.NET Core这样的新技术,对象类型在System.Runtime中定义。使用这两种方法,通常会得到对象和其他核心类型的副本。

.NET Standard 2.0改变了这种行为。.NET标准定义了一个API集,而不是一个实现。API的完整实现需要在.NET平台(如.NET Framework和.NET Core)中完成。标准是使用类型转发实现的,它将标准的类型转发到具体的实现中。列出.NET标准中类型的新库是NetStandard.dll。这个库对每个平台都不一样。NetStandard.dll列出类型,但不包含任何实现。相反,这个库包含了特定实现的类型转发器。例如,在.NET Framework项目中添加.NET标准库时,NetStandard.dll会自动引用,并包含从System.Console类到mscorlib程序集的一个类型转发器:

   .class extern forwarder System.Console
        {
        .assembly extern mscorlib
        }

因此,对于.NET Core项目,NetStandard.dll包含从System.Console到库System.Console的重定向。

   .class extern forwarder System.Console
        {
        .assembly extern System.Console
        }

下面使用类Legacy创建一个旧.NET Framework库来实现这一点。方法ConsoleMessage和WindowsMessage只写入输出。ConsoleMessage把输出写进控制台。WindowsMessage利用.NET Framework库System.Windows.Forms,并打开一个消息框。另外,ShowConsoleType方法提供了Console类来源的信息:

    public class Legacy
    {
        public static void ConsoleMessage(string message)
        {
            Console.WriteLine($"From the .NET Framework Lib: {message}");
        }
        public static void ShowConsoleType()
        {
            Console.WriteLine($"The type {nameof(Console)} is from {Assembly.GetAssembly(typeof(Console)).FullName}");
        }
        public static void WindowsMessage(string message)
        {
            MessageBox.Show($"Windows Forms: {message}");
        }
    }

在这个场景中需要注意的是,Console类是.NET标准的一部分,但是MessageBox类不是。Windows Forms是特定于Windows的,不会成为.NET标准的一部分。

接下来,.NET标准库引用这个.NET Framework库。通过添加对项目的引用,可以以简单的方式处理它。项目文件包含一个对.NET Framework库的ProjectReference:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\NetFrameworkClassLibrary\NetFrameworkClassLibrary.csproj" />
  </ItemGroup>

</Project>

在.NET标准库中定义的Wrapper类只是将方法调用转发到.NET Framework库。当引用.NET Framework库时,构建.NET标准库就没有编译错误:

    public class Wrapper
    {
        public static void ConsoleMessage(string message) => Legacy.ConsoleMessage(message);
        public static void WindowMessage(string message) => Legacy.WindowsMessage(message);
        public static void ShowConsoleType() => Legacy.ShowConsoleType();
    }

接下来,.NET Core控制台应用程序使用了Wrapper类。Program类调用了三个方法。但是,当调用WindowMessage方法时,检查FileNotFoundException异常的处理:

        static void Main(string[] args)
        {
            Wrapper.ConsoleMessage("Hello from .NET Core");
            Wrapper.ShowConsoleType();
            try
            {
                Wrapper.WindowMessage("Hello from .NET Core");
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

应用程序的运行结果如下所示。Console类来自System.Console程序集。调用WindowsMessage会导致一个FileNotFoundException,因为找不到System.Windows.Forms程序集:

From the .NET Framework Lib: Hello from .NET Core
The type Console is from System.Console, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Could not load file or assembly 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. 系统找不到指定的文件。

在创建.NET Framework控制台应用程序时,不需要将WindowsMessage方法的调用封装到try/catch处理程序中,因为System.Windows.Forms是可用的:

        static void Main(string[] args)
        {
            Wrapper.ConsoleMessage("Hello form the .NET Framework");
            Wrapper.ShowConsoleType();
            Wrapper.WindowMessage("Hello form the .NET Framework");
        }

运行此应用程序会表明,Console类来自于mscrolib程序集并为MessageBox弹出一个窗口:

From the .NET Framework Lib: Hello form the .NET Framework
The type Console is from mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

警告:

很容易从.NET Core应用程序中引用.NET Framework程序集,并构建应用程序。但是,这并不意味着.NET Framework程序集的每个功能都是可用的——只有.NET标准库中定义的类型可用。使用这些类型就可以在Linux上使用.NET Framewok库。

通常,如果类型不可用,则最好选择编译错误。把.NET Framework库重建为.NET标准库,就提供了这个特性。如果没有可用的源代码,比如来自还未提供.NET标准库的供应商,仍然可以使用.NET Core中的库。要检查二进制文件,并确定哪些类型与哪个.NET标准库不兼容,可以使用.NET可移植性分析器(.NET Portability Analyzer),它可用作命令行工具和Visual Studio扩展(参见https://github.com/microsoft/dotnet-apiport)。

在示例.NET Framework库中运行.NET可移植性分析器,展示了关于支持特性类型和成员的平台版本的信息,如下表所示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值