C# 中的 using 關鍵字有三種截然不同的用法,分別作用於命名空間匯入、型別別名、資源釋放區塊。許多開發者只熟悉前兩種,忽略 using statement 的資源管理威力。本文將三大用法一次釐清,並提供進階模式與最佳實務。
| 用法 | 語法位置 | 目的 | 關鍵字角色 |
|---|---|---|---|
| using 指令 | 檔案頂端 / 命名空間區塊 | 匯入命名空間,簡化型別名稱 | 指令 |
| using 別名 | 檔案頂端 / 命名空間區塊 | 為型別/命名空間建立短名稱,解決名稱衝突 | 指令 + = |
| using 陳述式 | 方法區塊內 | 確保 IDisposable 物件離開區塊時自動 Dispose() |
陳述式 |
using System;
using System.Collections.Generic;
using System.Linq;
using static System.Console;
using static System.Math;
// 直接呼叫靜態成員
WriteLine(Sqrt(2)); // 等同 Console.WriteLine(Math.Sqrt(2))
// GlobalUsings.cs (專案層級,通常由 SDK 隱含產生)
global using System;
global using System.Collections.Generic;
// 專案所有檔案自動可用,無需每檔重複寫
// 兩個命名空間都有 MyClass
using Alias1 = NameSpace1.MyClass;
using Alias2 = NameSpace2.MyClass;
// 使用時明確指定
var obj1 = new Alias1();
var obj2 = new Alias2();
using StringList = System.Collections.Generic.List<string>;
using IntDict = System.Collections.Generic.Dictionary<int, string>;
var names = new StringList { "A", "B" };
var map = new IntDict { [1] = "One" };
using Net = System.Net;
using NetHttp = System.Net.Http;
var client = new NetHttp.HttpClient();
typeof、nameof、屬性標記等語法位置。
// 語法糖:編譯器展開為 try/finally
using (var resource = new Resource())
{
resource.DoWork();
}
// 等價於
Resource resource = new Resource();
try
{
resource.DoWork();
}
finally
{
if (resource != null) resource.Dispose();
}
// 傳統巢狀
using (var fs = File.OpenRead("a.txt"))
using (var sr = new StreamReader(fs))
using (var sw = new StreamWriter("b.txt"))
{
// ...
}
// C# 8+ 簡潔語法(無大括號,區塊結束自動 Dispose)
using var fs = File.OpenRead("a.txt");
using var sr = new StreamReader(fs);
using var sw = new StreamWriter("b.txt");
// 方法結束時依宣告順序反向 Dispose
public class TransactionScope : IDisposable
{
private bool _completed;
public void Complete() => _completed = true;
public void Dispose()
{
if (!_completed) Rollback();
else Commit();
}
}
// 使用
using (var tx = new TransactionScope())
{
DoWork();
tx.Complete(); // 標記成功,Dispose 時 Commit
}
// 1. global using (專案層級,隱含)
// 2. 檔案層級 using 指令
using System;
using System.Threading.Tasks;
// 3. using 別名(僅在衝突或簡化泛型時)
using Config = MyApp.Configuration.Settings;
// 4. using static(僅極常用靜態類別)
using static System.Console;
IDisposable 的物件,必須在 using 區塊中建立並使用(Stream、DbConnection、HttpClient、GDI+ 物件、COM 物件等)。using 區塊外呼叫 Dispose() 再進入區塊(雙重 Dispose 可能拋出例外)。await using (C# 8+) 配合 IAsyncDisposable。
// ❌ 錯誤:物件在 using 區塊外建立
var conn = new SqlConnection(cs);
using (conn) { ... } // 若這裡拋出例外,外層 conn 可能未 Dispose
// ✅ 正確:在 using 內建立
using (var conn = new SqlConnection(cs)) { ... }
// ❌ 錯誤:using 區塊內重新指派變數
using (var fs = File.OpenRead("a.txt"))
{
fs = File.OpenRead("b.txt"); // 原本的 fs 失去參考,無法 Dispose!
}
// ✅ 正確:分開宣告
using var fs1 = File.OpenRead("a.txt");
using var fs2 = File.OpenRead("b.txt");
using System;
using System.IO;
using System.Data.SqlClient;
// using 別名解決泛型冗長
using UserMap = System.Collections.Generic.Dictionary<int, string>;
namespace UsingDemo
{
class Program
{
static void Main()
{
// using 陳述式管理多重資源
using (var conn = new SqlConnection("Data Source=.;Initial Catalog=Test;Integrated Security=True"))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "SELECT Id, Name FROM Users";
using (var reader = cmd.ExecuteReader())
{
var users = new UserMap();
while (reader.Read())
{
users[reader.GetInt32(0)] = reader.GetString(1);
}
// 顯示結果
foreach (var kv in users)
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
} // 依序 Dispose reader, cmd, conn
// C# 8+ using var
using var fs = File.CreateText("log.txt");
fs.WriteLine("Done");
}
}
}
global using、using static 簡化專案結構。IDisposable 物件確定性釋放。C# 8+ using var、await using 更簡潔安全。using。閱讀程式碼時務必依語境區分。