new 是 C# 中出現頻率最高的關鍵字之一,但許多開發者只熟悉「建立物件」這一面。實際上它還扮演「隱藏基底成員」修飾詞與「泛型型別約束」兩種完全不同的角色。本文將三大用法一次釐清,並附上常見誤區與最佳實踐。
// 參考型別:在堆疊配置記憶體,回傳參考
var list = new List();
// 實值型別:在堆疊/內嵌配置,回傳實值副本
var point = new Point(10, 20);
// 匿名型別
var anon = new { Name = "Tom", Age = 25 };
// 陣列
var arr = new int[3] { 1, 2, 3 };
var arr2 = new[] { "A", "B", "C" }; // 型別推論
// 物件初始化器(C# 3.0+)
var user = new User { Id = 1, Name = "Jerry" };
// 集合初始化器
var dict = new Dictionary { [1] = "One", [2] = "Two" };
重點:new 運算子不能被多載,行為由 CLR 固定;結構體若未顯式定義建構函式,會自動擁有無參建構函式(將所有欄位歸零)。
public class Base
{
public virtual void Show() => Console.WriteLine("Base.Show");
public void Print() => Console.WriteLine("Base.Print");
}
public class Derived : Base
{
// 1. 隱藏虛擬方法(不加 new 會產生 CS0108 警告)
public new void Show() => Console.WriteLine("Derived.Show");
// 2. 隱藏非虛擬方法(同樣建議加 new 消除警告)
public new void Print() => Console.WriteLine("Derived.Print");
}
Base b = new Derived();
b.Show(); // 輸出:Base.Show (虛擬方法走多型,呼叫基底實作)
b.Print(); // 輸出:Base.Print(非虛擬方法靜態繫結,呼叫基底實作)
Derived d = new Derived();
d.Show(); // 輸出:Derived.Show
d.Print(); // 輸出:Derived.Print
| 特性 | new (隱藏) | override (覆寫) |
|---|---|---|
| 基底成員需標記 | 不需要 | 必須 virtual/abstract/override |
| 多型行為 | 無(靜態繫結) | 有(動態派發) |
| 適用目標 | 任何成員(方法、屬性、欄位、事件) | 僅虛擬/抽象方法、屬性、索引子、事件 |
| 版本控制意圖 | 「我要取代這個成員,基底版本不再相關」 | 「我要延伸/修改基底行為」 |
new 能改變基底類別行為 → 錯誤,基底類別程式碼完全不受影響。new 隱藏介面成員 → 會導致介面呼叫時仍執行介面預設實作,極易產生 Bug。// 僅允許擁有 public 無參建構函式的具象類別
public class Repository where T : class, new()
{
public T Create() => new T(); // 編譯器保證 T 可被 new()
}
// 使用範例
var repo = new Repository(); // User 必須有 public User() {}
var user = repo.Create();
where T : class, new() // 參考型別 + 無參建構函式
where T : struct, new() // 實值型別(結構體隱含無參建構函式,new() 多餘但合法)
where T : IEntity, new() // 介面 + 無參建構函式
where T : BaseClass, new() // 基底類別 + 無參建構函式
where T : notnull, new() // 非 nullable 型別 + 無參建構函式 (C# 8.0+)
約束順序固定:class/struct/notnull → 基底類別 → 介面 → new()。
| 情境 | 建議 |
|---|---|
| 建立物件實例 | 直接使用 new Type(),優先考慮工廠模式/依賴注入解耦 |
| 衍生類別想「完全取代」基底非虛擬成員 | 加 new 修飾詞並加上 XML 註解說明隱藏意圖 |
| 衍生類別想「擴充」基底虛擬成員 | 基底加 virtual,衍生加 override |
| 泛型工廠/容器需實例化 T | 加 where T : new(),並文件化「T 必須有公用無參建構函式」 |
| 介面預設實作想被具體類別取代 | 不要用 new,改用顯式介面實作或重新設計介面 |
搞懂三者差異,才能在程式碼審查時精準溝通意圖,避免「以為覆寫其實隱藏」等低級錯誤。