當我們在 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 的檔案。如下圖:
// <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
類別,包含 File
、HttpClient
、Thread
等類別也都不用在其他 .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!
沒有留言: