使用 .NET CLI 開發程式庫

本文說明如何使用 .NET CLI 撰寫 .NET 的程式庫。 CLI 提供可在所有支援的作業系統上運作的有效率且低階體驗。 您仍然可以使用 Visual Studio 來建置程式庫,而且,如果那是您偏好的體驗,請參閱 Visual Studio 指南

必要條件

您必須在機器上安裝 .NET SDK

針對這份文件中處理 .NET Framework 版本的一節,您需要在 Windows 電腦上安裝 .NET Framework

此外,如果您想支援更舊的 .NET Framework 目標,就必須從 .NET Framework 下載頁面安裝目標套件或開發人員套件。 請參閱這張表格︰

.NET Framework 版本 要下載的項目
4.6.1 .NET Framework 4.6.1 目標套件
4.6 .NET Framework 4.6 目標套件
4.5.2 .NET Framework 4.5.2 開發人員套件
4.5.1 .NET Framework 4.5.1 開發人員套件
4.5 適用於 Windows 8 的 Windows 軟體開發套件
4.0 適用於 Windows 7 的 Windows SDK 及 .NET Framework 4
2.0、3.0 和 3.5 .NET Framework 3.5 SP1 執行階段 (或 Windows 8+ 版本)

如何以 .NET 5+ 或 .NET Standard 為目標

若想控制專案的目標 Framework,可將其新增至專案檔 (.csproj.fsproj)。 如需相關指引,以了解如何選擇將 .NET 5+ 或 .NET Standard 設為目標,請參閱 .NET 5+ 和 .NET Standard

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
</Project>

如果您想要將目標設為 .NET Framework 4.0 或以下版本,或者想要使用 .NET Framework 中提供、但 .NET Standard 中未提供的 API (例如 System.Drawing),請閱讀下列各節,並了解如何使用多目標。

如何以 .NET Framework 為目標

注意

這些指示假設您已在機器上安裝 .NET Framework。 請參閱安裝相依性的必要條件

請記住,這裡使用的一些 .NET Framework 版本已不再支援。 如需不支援的版本,請參閱 .NET Framework Support Lifecycle Policy FAQ (.NET Framework 支援週期原則 FAQ)。

如果您想要達到開發人員和專案數目上限,請使用 .NET Framework 4.0 作為基準目標。 若要將 .NET Framework 設為目標,請先從要支援的 .NET Framework 版本所對應的正確目標 Framework Moniker (TFM) 開始。

.NET Framework 版本 TFM
.NET Framework 2.0 net20
.NET Framework 3.0 net30
.NET Framework 3.5 net35
.NET Framework 4.0 net40
.NET Framework 4.5 net45
.NET Framework 4.5.1 net451
.NET Framework 4.5.2 net452
.NET Framework 4.6 net46
.NET Framework 4.6.1 net461
.NET Framework 4.6.2 net462
.NET Framework 4.7 net47
.NET Framework 4.8 net48

您接著將這個 TFM 插入專案檔的 TargetFramework 區段。 舉例來說,以下是如何撰寫以 .NET Framework 4.0 為目標的程式庫:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net40</TargetFramework>
  </PropertyGroup>
</Project>

就這麼簡單! 雖然這是針對 .NET Framework 4 而編譯的,但您仍可在較新版的 .NET Framework 上使用程式庫。

如何使用多目標

注意

下列指示假設您已在電腦上安裝 .NET Framework。 請參閱必要條件小節,了解您需要安裝的相依性以及其下載位置。

當您的專案同時支援 .NET Framework 和 .NET 時,可能需要將目標設為舊版 .NET Framework。 在這個案例中,如果您想要為較新的目標使用較新的 API 和語言建構,請在程式碼中使用 #if 指示詞。 您也可能需要針對設為目標的每個平台新增不同的套件和相依性,以包含每個案例所需的不同 API。

例如,假設您的程式庫透過 HTTP 執行網路作業。 針對 .NET Standard 和 .NET Framework 4.5 版或更高版本,您可以使用 System.Net.Http 命名空間中的 HttpClient 類別。 不過,舊版 .NET Framework 沒有 HttpClient 類別,因此您可以改用這些項目之 System.Net 命名空間中的 WebClient 類別。

專案檔如下所示:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net40;net45</TargetFrameworks>
  </PropertyGroup>

  <!-- Need to conditionally bring in references for the .NET Framework 4.0 target -->
  <ItemGroup Condition="'$(TargetFramework)' == 'net40'">
    <Reference Include="System.Net" />
  </ItemGroup>

  <!-- Need to conditionally bring in references for the .NET Framework 4.5 target -->
  <ItemGroup Condition="'$(TargetFramework)' == 'net45'">
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Threading.Tasks" />
  </ItemGroup>
</Project>

您可在這裡發現三個主要變更︰

  1. TargetFramework 節點已取代為 TargetFrameworks,而且其內表示三個 TFM。
  2. 提取至一個 .NET Framework 參考的 net40 目標有一個 <ItemGroup> 節點。
  3. 提取至兩個 .NET Framework 參考的 net45 目標有一個 <ItemGroup> 節點。

前置處理器符號

建置系統會得知 #if 指示詞中所使用的下列前置處理器符號︰

目標 Framework 符號 其他符號
(適用於 .NET 5+ SDK)
平台符號 (僅可於
指定 OS 特定 TFM 後使用)
.NET Framework NETFRAMEWORK, NET48, NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, NET40, NET35, NET20 NET48_OR_GREATERNET472_OR_GREATERNET471_OR_GREATERNET47_OR_GREATERNET462_OR_GREATERNET461_OR_GREATERNET46_OR_GREATERNET452_OR_GREATERNET451_OR_GREATERNET45_OR_GREATERNET40_OR_GREATERNET35_OR_GREATERNET20_OR_GREATER
.NET Standard NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0 +NETSTANDARD2_1_OR_GREATERNETSTANDARD2_0_OR_GREATERNETSTANDARD1_6_OR_GREATERNETSTANDARD1_5_OR_GREATERNETSTANDARD1_4_OR_GREATERNETSTANDARD1_3_OR_GREATERNETSTANDARD1_2_OR_GREATERNETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (和 .NET Core) NETNET8_0NET7_0NET6_0NET5_0NETCOREAPPNETCOREAPP3_1NETCOREAPP3_0NETCOREAPP2_2NETCOREAPP2_1NETCOREAPP2_0NETCOREAPP1_1NETCOREAPP1_0 NET8_0_OR_GREATER, NET7_0_OR_GREATER, NET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, NETCOREAPP1_1_OR_GREATER, NETCOREAPP1_0_OR_GREATER ANDROIDBROWSERIOSMACCATALYSTMACOSTVOSWINDOWS
[OS][version] (例如 IOS15_1)、
[OS][version]_OR_GREATER (例如 IOS15_1_OR_GREATER)

注意

  • 無論您的目標版本為何,系統都會定義無版本符號。
  • 系統只會針對您設定為目標的版本定義版本特定符號。
  • 系統會針對您的目標版本和所有更早版本定義 <framework>_OR_GREATER 符號。 舉例來說,如果您要以 .NET Framework 2.0 為目標,則會定義下列符號:NET20NET20_OR_GREATERNET11_OR_GREATERNET10_OR_GREATER
  • NETSTANDARD<x>_<y>_OR_GREATER 符號只會針對 .NET Standard 目標定義,而不是針對實作 .NET Standard 的目標,例如 .NET Core 和 .NET Framework。
  • 這些與 MSBuild TargetFramework 屬性NuGet 所使用的目標 Framework Moniker (TFM) 不同。

以下是利用個別目標之條件式編譯的範例︰

using System;
using System.Text.RegularExpressions;
#if NET40
// This only compiles for the .NET Framework 4 targets
using System.Net;
#else
 // This compiles for all other targets
using System.Net.Http;
using System.Threading.Tasks;
#endif

namespace MultitargetLib
{
    public class Library
    {
#if NET40
        private readonly WebClient _client = new WebClient();
        private readonly object _locker = new object();
#else
        private readonly HttpClient _client = new HttpClient();
#endif

#if NET40
        // .NET Framework 4.0 does not have async/await
        public string GetDotNetCount()
        {
            string url = "https://www.dotnetfoundation.org/";

            var uri = new Uri(url);

            string result = "";

            // Lock here to provide thread-safety.
            lock(_locker)
            {
                result = _client.DownloadString(uri);
            }

            int dotNetCount = Regex.Matches(result, ".NET").Count;

            return $"Dotnet Foundation mentions .NET {dotNetCount} times!";
        }
#else
        // .NET Framework 4.5+ can use async/await!
        public async Task<string> GetDotNetCountAsync()
        {
            string url = "https://www.dotnetfoundation.org/";

            // HttpClient is thread-safe, so no need to explicitly lock here
            var result = await _client.GetStringAsync(url);

            int dotNetCount = Regex.Matches(result, ".NET").Count;

            return $"dotnetfoundation.org mentions .NET {dotNetCount} times in its HTML!";
        }
#endif
    }
}

如果您使用 dotnet build 建置此專案,則會在 bin/ 資料夾下方發現三個目錄:

net40/
net45/
netstandard2.0/

每個資料夾都包含各個目標的 .dll 檔案。

如何測試 .NET 上的程式庫

重要的是一定要可以跨平台進行測試。 您可以使用現成的 xUnit 或 MSTest。 這兩者都很適合對 .NET 上的程式庫進行單元測試。 如何設定具有測試專案的方案,將取決於方案結構。 下列範例假設測試及來源目錄位於相同的最上層目錄中。

注意

這會使用一些 .NET CLI 命令。 如需詳細資訊,請參閱 dotnet newdotnet sln

  1. 設定方案。 您可以使用下列命令:

    mkdir SolutionWithSrcAndTest
    cd SolutionWithSrcAndTest
    dotnet new sln
    dotnet new classlib -o MyProject
    dotnet new xunit -o MyProject.Test
    dotnet sln add MyProject/MyProject.csproj
    dotnet sln add MyProject.Test/MyProject.Test.csproj
    

    這會建立專案,並將它們同時連結在方案中。 您的 SolutionWithSrcAndTest 目錄應該如下所示:

    /SolutionWithSrcAndTest
    |__SolutionWithSrcAndTest.sln
    |__MyProject/
    |__MyProject.Test/
    
  2. 巡覽至測試專案的目錄,並將參考從 MyProject 新增至 MyProject.Test

    cd MyProject.Test
    dotnet add reference ../MyProject/MyProject.csproj
    
  3. 還原套件並建置專案︰

    dotnet restore
    dotnet build
    
  4. 執行 dotnet test 命令,確認已執行 xUnit。 如果您選擇使用 MSTest,則應該會改為執行 MSTest 主控台執行器。

就這麼簡單! 您現在可以使用命令列工具在所有平台上測試程式庫。 若要在設定好所有項目之後立即繼續測試,測試程式庫就會非常簡單︰

  1. 對程式庫進行變更。
  2. 在測試目錄中,從命令列中使用 dotnet test 命令來執行測試。

當您叫用 dotnet test 命令時,將會自動重建程式碼。

如何使用多個專案

較大程式庫的常見需求是將功能放在不同的專案中。

假設您想要建置可在慣用 C# 和 F# 中取用的程式庫。 這表示您程式庫的取用者會透過對 C# 或 F# 而言很自然的方式取用程式庫。 例如,在 C# 中,您可能會如下使用程式庫︰

using AwesomeLibrary.CSharp;

public Task DoThings(Data data)
{
    var convertResult = await AwesomeLibrary.ConvertAsync(data);
    var result = AwesomeLibrary.Process(convertResult);
    // do something with result
}

在 F# 中,它可能如下所示:

open AwesomeLibrary.FSharp

let doWork data = async {
    let! result = AwesomeLibrary.AsyncConvert data // Uses an F# async function rather than C# async method
    // do something with result
}

這類使用案例表示針對 C# 和 F#,正在存取的 API 必須具有不同的結構。 完成這項作業的常見方法將程式庫的所有邏輯都納入核心專案,而且 C# 和 F# 專案定義呼叫該核心專案的 API 層。 區段的其餘部分將使用下列名稱︰

  • AwesomeLibrary.Core - 內含程式庫所有邏輯的核心專案
  • AwesomeLibrary.CSharp - 專案,具有公用 API 可供在 C# 中使用
  • AwesomeLibrary.FSharp - 專案,具有公用 API 可供在 F# 中使用

您可以在終端機中執行下列命令,以產生與本指南相同的結構︰

mkdir AwesomeLibrary && cd AwesomeLibrary
dotnet new sln
mkdir AwesomeLibrary.Core && cd AwesomeLibrary.Core && dotnet new classlib
cd ..
mkdir AwesomeLibrary.CSharp && cd AwesomeLibrary.CSharp && dotnet new classlib
cd ..
mkdir AwesomeLibrary.FSharp && cd AwesomeLibrary.FSharp && dotnet new classlib -lang "F#"
cd ..
dotnet sln add AwesomeLibrary.Core/AwesomeLibrary.Core.csproj
dotnet sln add AwesomeLibrary.CSharp/AwesomeLibrary.CSharp.csproj
dotnet sln add AwesomeLibrary.FSharp/AwesomeLibrary.FSharp.fsproj

這項操作會新增上述三個專案,以及將三者連結在一起的解決方案檔。 建立解決方案檔及連結專案可讓您從最上層還原與建置專案。

專案對專案參考

參考專案的最佳方式是使用 .NET CLI 來新增專案參考。 從 AwesomeLibrary.CSharpAwesomeLibrary.FSharp 專案目錄中,您可以執行下列命令︰

dotnet add reference ../AwesomeLibrary.Core/AwesomeLibrary.Core.csproj

AwesomeLibrary.CSharpAwesomeLibrary.FSharp 的專案檔現在參考 AwesomeLibrary.Core 作為 ProjectReference 目標。 檢查專案檔並查看其中的下列內容,即可確認這項作業︰

<ItemGroup>
  <ProjectReference Include="..\AwesomeLibrary.Core\AwesomeLibrary.Core.csproj" />
</ItemGroup>

如果您不想要使用 .NET CLI,則可以手動將這個區段新增至每個專案檔。

建構方案

多專案方案的另一個重要部分是建立不錯的整體專案結構。 不過,您可以視需要組織程式碼,而且只要您使用 dotnet sln add 將每個專案連結至方案檔,就可以在方案層級執行 dotnet restoredotnet build