new()约束是编译期强制的类型契约,要求泛型参数必须有public无参构造函数;它不生成IL指令,而是标记元数据,并使new T()编译为Activator.CreateInstance()调用。

泛型约束不是语法糖,而是编译期强制的类型契约;没加 new() 就敢写 new T(),编译器直接拦下——它不给你运行时崩溃的机会。
为什么 new T() 必须搭配 where T : new()
编译器不知道 T 到底是什么类型,更不确定它有没有 public 无参构造函数。没有 new() 约束时,new T() 是非法操作,哪怕你传入的 class Person { } 看似“显然有默认构造函数”,编译器也不会做这种推断。
加上 where T : new() 后,编译器才允许生成调用逻辑,并在绑定泛型实参时检查:该类型是否公开声明了无参构造函数(或 struct 隐式满足)。
- 抽象类、接口、委托类型、只定义了带参构造函数的 class —— 全部被拒
- struct 不需要显式定义构造函数,但
where T : new()仍合法,因为它的零值初始化语义等价于“可无参构造” - 如果类只写了
public Person(int id),必须补上public Person() { }才能通过约束校验
new() 约束在 IL 层到底干了什么
它本身不生成任何 IL 指令,而是在元数据中标记该泛型参数受构造函数约束。真正干活的是 new T() 这一行:C# 编译器把它翻译成对 System.Activator.CreateInstance<t>()</t> 的调用,而不是直接 emit call instance void .ctor()。
这意味着——即使你用了 new() 约束,底层仍是反射路径。性能敏感路径(如高频对象池、渲染循环内创建)要警惕这点。
- 不是 JIT 优化后就变成直接构造;.NET 6+ 对
Activator.CreateInstance<t>()</t>有缓存,但首次调用仍有反射开销 - 若需极致性能,应改用表达式树预编译构造器委托,或直接传入
Func<t></t>工厂 -
where T : struct的new T()是例外:它被 JIT 优化为零初始化(initobj),不走Activator
组合约束时 new() 的位置和兼容性陷阱
new() 必须放在 where 子句所有其他约束之后,且不能和 class 或 struct 约束共存(因为它们已隐含构造能力,再加 new() 会冗余甚至冲突)。
常见错误组合:
-
where T : class, new(), IDisposable✅ 合法:引用类型 + 可实例化 + 可释放 -
where T : struct, new()❌ 编译报错:struct已隐含new()语义,重复声明不被允许 -
where T : Enum, new()❌ 编译报错:枚举类型无法定义构造函数,new()约束与Enum天然互斥 -
where T : Delegate✅ 合法(C# 7.3+),但不能加new()—— 委托类型不支持无参构造
别以为加了 new() 就万事大吉
它只保证“能构造”,不保证“构造得安全”。比如 class Dangerous { public Dangerous() { throw new InvalidOperationException(); } } 完全满足 where T : new(),但 new T() 运行时照样崩。
更隐蔽的问题是:构造函数执行顺序、静态字段初始化时机、跨程序集可见性(internal 构造函数在另一 assembly 中不被视为 public)——这些都不会被 new() 约束捕获。
真正要稳,得配合单元测试覆盖泛型实参的构造行为,而不是只依赖编译器那层纸面契约。


















