C# 泛型(Generics)的约束有哪些 - where关键字的高级用法

来源:这里教程网 时间:2026-02-21 17:36:05 作者:

泛型约束的核心作用

泛型约束(where子句)不是可选项,而是让编译器在编译期就验证类型安全的关键机制。它限制了泛型参数能接受哪些具体类型,从而允许你在泛型代码中调用这些类型支持的操作——比如调用方法、访问属性、使用 new() 创建实例,或者进行特定的类型转换。

六类常见 where 约束及其典型用途

class / struct:限定引用类型或值类型

where T : class
—— 确保 T 是类、接口、委托或数组(即非值类型),可用于判空或作为引用传递
where T : struct
—— 确保 T 是不可为 null 的值类型(不含 Nullable),常用于高性能数值计算场景

基类或接口约束:启用成员访问

where T : IComparable
—— 可直接调用
CompareTo()
where T : Animal, ICloneable
—— 支持多约束,T 必须继承 Animal 并实现 ICloneable
注意:基类必须写在接口前面,且只能有一个基类

构造函数约束

new()

where T : new()
—— 要求 T 具有无参公共构造函数
常与其它约束组合使用,例如
where T : class, new()
,适用于工厂模式或 ORM 实体创建
不支持带参构造函数;若需更灵活初始化,应改用工厂委托或抽象工厂

协变与逆变约束(仅适用于泛型接口/委托)

in T
(逆变):T 仅作输入参数,如
IComparer<in t></in>
,允许
IComparer<object></object>
赋值给
IComparer<string></string>
out T
(协变):T 仅作返回值,如
IEnumerable<out t></out>
,允许
IEnumerable<string></string>
赋值给
IEnumerable<object></object>
协变/逆变需显式标注,且受类型安全性严格限制(不能同时 in 和 out 同一类型)

组合约束与实用技巧

多个约束可用逗号分隔,顺序有要求:基类 → 接口 → new()

正确:
where T : Product, IValidatable, new()
错误:
where T : new(), IValidatable, Product
(编译失败)

泛型类型参数还可约束为另一个泛型参数:

class Container<t u> where T : class where U : T</t>
—— 表示 U 必须是 T 或其派生类
这在构建类型安全的容器、策略链或泛型树结构时很实用

约束不是万能的 —— 注意边界情况

约束只影响编译期检查,运行时仍可能遇到装箱/拆箱、null 引用或隐式转换问题

where T : class
不排除 nullable 引用类型(C# 8+ 默认开启 nullable reference types 后才可区分)
where T : struct
排除了
Nullable<int></int>
(即
int?
),因为它本质是泛型结构体,但编译器特殊处理,不能直接用在
struct
约束中
接口约束无法保证具体实现行为(比如
IComparable.CompareTo
是否真正按预期比较),仍需运行时逻辑校验

基本上就这些。合理使用 where 约束,能让泛型既保持通用性,又获得接近非泛型代码的类型提示和安全保证。

相关推荐