許多開發者以為 where T : new() 讓泛型內部 new T() 像非泛型一樣直接呼叫建構函式。**實際上:編譯器產生的 IL 仍呼叫 Activator.CreateInstance<T>() (反射機制)。**
T 擁有 公用無參數建構函式。若無,編譯錯誤 CS0310/CS0311。new T() 編譯為 call System.Activator::CreateInstance<!!T>()。
// 原始碼
public class Factory where T : new()
{
public T Create() => new T();
}
// 編譯後 IL (簡化)
.method public hidebysig instance !!T Create() cil managed
{
.maxstack 1
L_0000: call !!0 [System.Runtime]System.Activator::CreateInstance<!!T>()
L_0005: ret
}
// 對比:非泛型 new()
.method public hidebysig instance class MyClass CreateNonGeneric() cil managed
{
.maxstack 1
L_0000: newobj instance void MyClass::.ctor() // 直接呼叫建構函式
L_0005: ret
}
| 方式 | 機制 | 相對速度 | 備註 |
|---|---|---|---|
new T() (有 new() 約束) | Activator.CreateInstance (反射 + 快取) | ~10-50x 慢於直接 new | 首次呼叫較慢,後續透過委派快取加速 |
new ConcreteType() | 直接 newobj IL 指令 | 基準 (最快) | |
Activator.CreateInstance<T>() 手動呼叫 | 相同 | 相同 | |
編譯表達式樹 Expression.New 快取委派 | 動態編譯委派 | 接近直接 new | 高頻建立場景優選 |
using System;
using System.Linq.Expressions;
public static class FastActivator where T : new()
{
// 靜態建構子僅執行一次,編譯 Lambda 為強型別委派
private static readonly Func Creator = CreateCreator();
private static Func CreateCreator()
{
var newExpr = Expression.New(typeof(T).GetConstructor(Type.EmptyTypes)!);
return Expression.Lambda>(newExpr).Compile();
}
public static T CreateInstance() => Creator();
}
// 使用
var instance = FastActivator.CreateInstance(); // 極快,無反射開銷
| 場景 | 建議方案 |
|---|---|
| 低頻建立 (工廠、Plugin 載入) | 直接 new T() + where T : new(),簡潔優先 |
| 高頻建立 (物件池、每幀實例化) | FastActivator<T> 編譯表達式樹快取委派 |
| 需傳參數建構 | 改用 Func<TArg, T> 工廠委派注入,移除 new() 約束 |
| 結構體高效能建立 | default(T) 或 new T() (結構體無反射開銷) |
where T : new() 是**編譯期語法糖**,提供型別安全保證,但執行期仍透過反射建立實例。理解此機制有助於在效能敏感場合做出正確優化選擇。