C#的泛型约束是通过where关键字对泛型类型参数施加的限制,用来指定类型参数必须满足的条件,确保泛型代码在编译阶段就能校验类型合法性,避免运行时出现类型相关的错误。

泛型约束的作用
泛型本身允许类型参数是任意类型,但在很多场景下我们需要类型参数具备特定的能力,比如必须实现某个接口、必须有无参构造函数等。如果没有约束,我们只能对类型参数做非常有限的操作,泛型约束就是用来解决这个问题的,它让泛型代码既保持灵活性,又具备类型安全性。
常见的泛型约束类型
C#支持多种泛型约束,不同的约束对应不同的类型要求,常见的约束类型如下:
| 约束类型 | 语法格式 | 作用说明 | |||
|---|---|---|---|---|---|
| 类约束 | where T : 类名 | 类型参数必须是该指定类或其派生类 | |||
| 接口约束 | where T : 接口名 | 类型参数必须实现指定的接口 | new()约束 | where T : new() | 类型参数必须有无参公共构造函数 |
| 值类型约束 | where T : struct | 类型参数必须是值类型 | |||
| 引用类型约束 | where T : class | 类型参数必须是引用类型 | |||
| 基类约束 | where T : 基类 | 类型参数必须是指定基类或其派生类 |
泛型约束的使用方法
在泛型类中使用约束
我们可以在定义泛型类时为类型参数添加约束,下面的示例定义了一个泛型仓储类,要求类型参数必须实现IEntity接口并且有无参构造函数:
// 定义实体接口
public interface IEntity
{
int Id { get; set; }
}
// 定义基础实体类,实现IEntity接口
public class BaseEntity : IEntity
{
public int Id { get; set; }
}
// 泛型仓储类,添加两个约束
public class Repository<T> where T : IEntity, new()
{
// 存储实体的列表
private List<T> _items = new List<T>();
// 添加实体,因为T实现了IEntity,所以可以直接访问Id属性
public void Add(T entity)
{
_items.Add(entity);
Console.WriteLine($"添加实体,Id为:{entity.Id}");
}
// 创建新实体,因为T有new()约束,所以可以调用无参构造函数
public T CreateNew()
{
return new T();
}
}
在泛型方法中使用约束
泛型方法同样可以添加约束,下面的示例定义了一个泛型方法,要求类型参数必须是值类型:
public class Utility
{
// 泛型方法,约束T为值类型
public static T Add<T>(T a, T b) where T : struct
{
// 值类型可以直接做加法操作(这里以int为例,实际场景可能需要更复杂的处理)
dynamic x = a;
dynamic y = b;
return x + y;
}
}
// 调用示例
class Program
{
static void Main()
{
int result = Utility.Add<int>(1, 2);
Console.WriteLine(result); // 输出3
}
}
多个约束的组合使用
一个类型参数可以同时添加多个约束,多个约束之间用逗号分隔,需要注意的是new()约束必须放在所有约束的最后:
// 定义示例接口
public interface ILog
{
void Write(string message);
}
// 定义示例类
public class Logger : ILog
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
// 泛型方法,同时添加引用类型约束、接口约束和new()约束
public static void Process<T>(string message) where T : class, ILog, new()
{
T logger = new T();
logger.Write(message);
}
// 调用示例
class Test
{
static void Main()
{
Process<Logger>("测试日志输出");
}
}
使用泛型约束的注意事项
- 同一个类型参数可以添加多个约束,但是new()约束必须放在约束列表的最后一位。
- 不能同时添加值类型约束和引用类型约束,因为值类型和引用类型是互斥的。
- 约束是针对类型参数的,不会影响泛型类或泛型方法的其他非类型参数部分的逻辑。
- 如果泛型类有多个类型参数,每个类型参数都可以单独添加自己的约束,语法为where T1 : 约束1 where T2 : 约束2。
泛型约束是C#泛型特性的重要补充,合理使用约束可以让泛型代码更加健壮,减少不必要的类型转换和运行时错误,在编写可复用的泛型组件时非常实用。