C#的泛型允许我们定义类型参数化的类、方法、接口等,而泛型约束通过where关键字限制类型参数的可用类型,既能保证类型安全,又能让类型参数调用特定的成员。
泛型约束的基本类型
C#支持多种类型的泛型约束,不同的约束对应不同的类型限制规则,以下是常用的约束类型:
1. 类约束
类约束要求类型参数必须是某个基类或者从该基类派生的类型,语法为where T : 基类名。
// 定义基类
public class BaseEntity
{
public int Id { get; set; }
}
// 泛型类,约束T必须是BaseEntity或其派生类
public class Repository<T> where T : BaseEntity
{
public void PrintId(T entity)
{
// 因为约束了T是BaseEntity的子类,所以可以直接访问Id属性
Console.WriteLine(entity.Id);
}
}
2. 接口约束
接口约束要求类型参数必须实现指定的接口,语法为where T : 接口名,一个类型参数可以实现多个接口约束。
// 定义接口
public interface ILog
{
void WriteLog(string message);
}
// 泛型方法,约束T必须实现ILog接口
public void ExecuteWithLog<T>(T instance) where T : ILog
{
instance.WriteLog("开始执行操作");
// 其他业务逻辑
instance.WriteLog("操作执行完成");
}
3. 引用类型约束
引用类型约束要求类型参数必须是引用类型,语法为where T : class,适用于需要操作对象引用的场景。
// 泛型方法,约束T必须是引用类型
public void CheckNull<T>(T value) where T : class
{
if (value == null)
{
Console.WriteLine("值为空");
}
else
{
Console.WriteLine("值不为空");
}
}
4. 值类型约束
值类型约束要求类型参数必须是值类型,语法为where T : struct,注意值类型约束不能和new()构造函数约束同时使用。
// 泛型方法,约束T必须是值类型
public T Add<T>(T a, T b) where T : struct
{
// 值类型可以直接使用动态类型相关操作,这里以int为例的逻辑
return (dynamic)a + b;
}
5. 构造函数约束
构造函数约束要求类型参数必须有无参数的公共构造函数,语法为where T : new(),方便在泛型内部创建类型实例。
// 泛型方法,约束T必须有公共无参构造函数
public T CreateInstance<T>() where T : new()
{
return new T();
}
// 使用示例
public class User
{
public string Name { get; set; }
}
// 调用时T为User,User有默认无参构造函数,符合约束
User user = CreateInstance<User>();
6. 裸类型约束
裸类型约束是指一个类型参数作为另一个类型参数的约束,语法为where T : U,要求T必须是U或者U的派生类。
// 泛型方法,约束T必须是U或者U的派生类
public void CopyData<T, U>(T source, U target) where T : U
{
// 这里T可以安全转换为U类型,调用U的相关成员
}
where关键字的高级用法
多重约束的组合使用
一个类型参数可以同时设置多个约束,多个约束之间用逗号隔开,需要注意约束的排列顺序:类约束或引用类型/值类型约束必须放在第一个,然后是接口约束,最后是new()构造函数约束(如果有)。
// 泛型类,T同时有多个约束:必须是BaseEntity的派生类、实现ILog接口、有公共无参构造函数
public class Service<T> where T : BaseEntity, ILog, new()
{
public void Process()
{
T instance = new T();
Console.WriteLine(instance.Id);
instance.WriteLog("处理完成");
}
}
多个类型参数的约束定义
如果泛型有多个类型参数,每个类型参数都可以单独设置where约束,每个where子句对应一个类型参数。
// 泛型类有两个类型参数,分别设置不同的约束
public class Mapper<TSource, TTarget>
where TSource : class
where TTarget : class, new()
{
public TTarget Map(TSource source)
{
TTarget target = new TTarget();
// 映射逻辑
return target;
}
}
泛型方法中的约束
where关键字不仅可以用在泛型类、泛型接口上,也可以用在一个单独的方法上,定义方法的泛型参数约束。
public class Utility
{
// 泛型方法,约束T必须是值类型
public static bool Compare<T>(T a, T b) where T : struct
{
return a.Equals(b);
}
}
// 调用泛型方法,传入int类型参数,符合值类型约束
bool result = Utility.Compare(1, 2);
泛型约束的使用场景和注意事项
泛型约束的核心作用是缩小类型参数的范围,让代码在编译期就能检查类型合法性,避免运行时出现无效类型转换的错误。比如在仓储模式中,我们通常约束实体类必须继承基类,保证所有实体都有统一的Id属性;在依赖注入场景中,约束服务类必须实现特定接口,保证服务的功能符合预期。
需要注意,泛型约束不能限制静态成员的使用,因为静态成员属于类型本身,和类型参数无关。另外,过多的约束会降低泛型的灵活性,所以定义约束时要平衡类型安全和复用性,只添加必要的约束。
| 约束类型 | 语法格式 | 作用说明 |
|---|---|---|
| 类约束 | where T : 基类名 | 类型参数必须是基类或其派生类 |
| 接口约束 | where T : 接口名 | 类型参数必须实现指定接口 |
| 引用类型约束 | where T : class | 类型参数必须是引用类型 |
| 值类型约束 | where T : struct | 类型参数必须是值类型 |
| 构造函数约束 | where T : new() | 类型参数必须有公共无参构造函数 |
| 裸类型约束 | where T : U | 类型参数T必须是U或其派生类 |
C#_Genericswhere关键字泛型约束类型参数约束修改时间:2026-06-27 21:34:06