C# 中的 where
关键字主要用在泛型约束(Generic Constraints)中,目的是对泛型类型参数限制其必须满足的条件,从而保证类型参数具备特定的能力或特性,增强类型安全和代码可读性。
约束写法 | 说明 | 适用场景举例 | C#版本要求 |
---|---|---|---|
where T : class |
类型参数必须是引用类型(类、接口、数组等) | 需要引用类型对象 | 所有版本 |
where T : struct |
类型参数必须是非空值类型(结构体,不能是Nullable) | 需要值类型且非可空 | 所有版本 |
where T : new() |
类型参数必须有公共无参构造函数 | 需要用 new T() 创建实例 |
所有版本 |
where T : BaseClass |
类型参数必须继承自指定基类 | 需要基类的成员或行为 | 所有版本 |
where T : IInterface |
类型参数必须实现指定接口 | 需要接口定义的行为 | 所有版本 |
where T : unmanaged |
类型参数必须是不含引用类型字段的非托管类型 | 需要直接内存操作、指针操作 | C# 7.3 及以后 |
where T : System.Enum |
类型参数必须是枚举类型 | 泛型操作枚举时 | C# 7.3 及以后 |
where T : System.Delegate |
类型参数必须是委托类型 | 泛型委托相关操作 | C# 7.3 及以后 |
约束顺序规则
基类约束必须放在最前面(如果有)
如果存在继承类的约束(如where T : BaseClass
),它必须写在所有接口约束之前。接口约束写在基类约束后面
可以有多个接口约束,写在基类约束之后,接口之间用逗号分隔。new()
构造函数约束必须写在最后
表示类型参数必须有无参构造函数,必须放在所有其他约束的最后。class
和struct
只能有一个,且不能和彼此同时使用where T : class
表示引用类型约束where T : struct
表示值类型约束
两者互斥。
- class和BaseClass约束不能同时出现
unmanaged
和其他值类型约束的结合
unmanaged
约束比struct
更严格,也只可单独使用。
约束类型 | 书写顺序 |
---|---|
基类约束、class约束、struct约束 | 最前面 |
接口约束 | 紧随基类约束后面 |
构造函数约束 (new() ) |
必须最后 |
class 或 struct |
与其他约束互斥,不可同时使用 |
Struct约束
struct
约束的限制和组合规则
struct
约束不能与class
约束同时出现(引用类型和值类型互斥)。struct
约束不能与基类约束一起使用(因为基类必须是引用类型)。struct
约束不能与接口约束一起使用(在早期C#版本中),但从C# 7.3 起允许和接口约束一起使用,即可以写:where T : struct, IComparable
struct
约束后面可以跟接口约束(C# 7.3及以后版本)。struct
约束后面也可以跟构造函数约束new()
,不过对struct
类型参数来说,编译器自动隐含有无参构造函数,new()
约束通常不必写。
合法示例:
public class Example<T> where T : struct, IComparable, new()
{
}