泛型约束(where子句)是编译期类型安全的关键机制,用于限定泛型参数可接受的类型,支持成员访问、实例化及协变/逆变等操作,共六类:class/struct、基类/接口、new()、in/out、组合约束及泛型参数约束。

泛型约束的核心作用
泛型约束(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,允许IComparer赋值给IComparer -
out T(协变):T 仅作返回值,如IEnumerable,允许IEnumerable赋值给IEnumerable - 协变/逆变需显式标注,且受类型安全性严格限制(不能同时 in 和 out 同一类型)
组合约束与实用技巧
多个约束可用逗号分隔,顺序有要求:基类 → 接口 → new()
- 正确:
where T : Product, IValidatable, new() - 错误:
where T : new(), IValidatable, Product(编译失败)
泛型类型参数还可约束为另一个泛型参数:
-
class Container—— 表示 U 必须是 T 或其派生类where T : class where U : T - 这在构建类型安全的容器、策略链或泛型树结构时很实用
约束不是万能的 —— 注意边界情况
约束只影响编译期检查,运行时仍可能遇到装箱/拆箱、null 引用或隐式转换问题
-
where T : class不排除 nullable 引用类型(C# 8+ 默认开启 nullable reference types 后才可区分) -
where T : struct排除了Nullable(即int?),因为它本质是泛型结构体,但编译器特殊处理,不能直接用在struct约束中 - 接口约束无法保证具体实现行为(比如
IComparable.CompareTo是否真正按预期比较),仍需运行时逻辑校验
基本上就这些。合理使用 where 约束,能让泛型既保持通用性,又获得接近非泛型代码的类型提示和安全保证。









