C# 10 的全域引用

本文要介紹 C# 10 新增的全域引用(global using)命名空間的語法,同時也會提及 .NET 6 SDK 的相關功能:隱含式全域引用(implicit global using)。

工具使用: Visual Studio 2022、.NET 6 SDK

當我們在 Visual Studio 中建立一個新的 Console 應用程式專案,目標框架選擇 .NET 6,並採用預設的專案名稱 ConsoleApp1,專案建立完成後,可以看到 Program.cs 檔案裡面只有一行程式碼,外加一行註解:

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

程式裡面使用了 Console.WriteLine(...) 卻沒有 using System 命名空間,這是使用了 .NET 6 的「隱含引用」(implicit using)功能。那麼,這些隱含引用的命名空間是隱藏在哪裡呢?

開啟專案的 .csproj 檔案,應該會看到裡面有一個 <ImplicitUsings> 元素:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

第 5 行的 <ImplicitUsings> 元素就是用來控制該專案是否開啟「隱含引用」的開關,預設為開啟(enable)。

於是,編譯專案時,就會在專案目錄下的「obj\Debug\目標框架\」底下自動產生一個名為 [專案名稱].GlobalUsings.g.cs 的檔案。如下圖:




開啟這個 [專案名稱].GlobalUsings.g.cs 檔案,便可以看到類似底下的內容:

// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

每行程式碼開頭的 global using 是 C# 10 新增的全域引用語法,而上面的程式片段裡面總共有七個全域引用的命名空間,這表示不僅 Console 類別,包含 FileHttpClientThread 等類別也都不用在其他 .cs 檔案中引用對應的命名空間,即可直接使用。如此一來,便可節省一些重複打字的時間。

值得一提的是,上列程式碼片段中的 global:: 指示詞是在告訴編譯器:其後面跟著的命名空間是全域(最上層)的命名空間,請不要解析成特定命名空間底下的子命名空間。這個指示詞並非必要,但如果碰到命名空間衝突的情形,便可以使用命名空間別名辨識符號 :: 來解決。

剛才展示的程式片段是來自預設的 Console 專案模板,如果是其他類型的專案模板,則會看到不同的內容。例如底下是建立 Blazor Server 專案時自動產生的 .GlobalUsings.g.cs 檔案的內容:

// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

現在我們知道了,原來「隱含引用」這項功能,背後其實使用了一種叫做 global using 的語法。那麼,我們是否可以「棄暗投明」,在自己的專案裡面明白地撰寫這些「全域引用」呢?答案是肯定的。

使用 C# 檔案來管理全域引用

我們可以使用一個 C# 檔案來集中管理 global using 語句,具體作法如下。

首先,開啟專案的 .csproj 檔案,把 <ImplicitUsings>enable</ImplicitUsings> 整行刪除,或將其屬性值改為 disable。也就是說,不要使用 Visual Studio 專案範本所提供的那些預設的全域命名空間。

接著在專案中加入一個 C# 檔案,通常命名為 GlobalUsings.cs。然後只要在這個檔案裡面使用 global using 來加入你想要套用至整個專案的命名空間就行了。參考下圖:


透過專案檔來管理全域引用

除了使用剛才示範的 GlobalUsings.cs 檔案,我們也可以在 .csproj 裡面使用 <Using> 元素來增加或移除全域的命名空間。參考以下範例:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>
    
    <ItemGroup>
      <Using Include="MyLib.Extensions" />
      <Using Remove="System.Net.Http" />
    </ItemGroup>    
</Project>

說明:

  • 第 5 行:<ImplicitUsings> 為 enable,表示要使用 .NET SDK 的隱含引用功能來自動產生稍早提過的 [專案名稱].GlobalUsings.g.cs 檔案。
  • 第 11 行:把 MyLib.Extensions 加入全域引用名單。
  • 第 12 行:把 System.Net.Http 從全域引用名單中移除。也就是說,如果 <ImplicitUsings> 所產生的全域引用名單裡面有 System.Net.Http,便將它移除。

我個人不是很喜歡編輯 XML,所以偏好使用一個 C# 檔案來管理整個專案的 global using 語句。如此一來,如果發現命名空間衝突,或者有任何疑慮時,只要打開我建立的那個 GlobalUsings.cs 檔案,便可一目瞭然。

重點整理

  • global using(全域引用)是 C# 10 新增的語法,其用途是將指定的命名空間套用於整個專案。如此一來,那些常用的命名空間可以只寫一次 using 語句,而不用在每一個 C# 檔案裡面重複寫。
  • 專案的 .csproj 檔案中的 <ImplicitUsings> 可用來控制是否啟用 .NET SDK 的「隱含引用」功能。若啟用,編譯專案的時候就會自動產生一個名為 [專案名稱].GlobalUsings.g.cs 的檔案,裡面有一些常用命名空間的 global using 語句。此外,.csproj 檔案裡面也可以透過 <Using> 元素來增加或移除全域引用的命名空間。
  • 我們也可以用一個 C# 檔案來集中管理全域引用的命名空間。
  • 全域引用的有效範圍是「這個專案」。換言之,A 專案裡面的 global using 語句不會影響到 B 專案或其他專案。

本文同步發布於 GitHub 上面的 LearningNotes

Happy learning!

沒有留言:

技術提供:Blogger.
回頂端⬆️