在C#的泛型编程中,泛型约束可以让开发者限定泛型参数必须满足的类型条件,其中限定泛型为类或者接口是最常用的约束场景,主要通过where关键字来实现。

泛型约束的基础语法
C#中使用where关键字为泛型参数添加约束,基本语法格式如下:
// 泛型类的约束语法
public class 类名<T> where T : 约束条件
{
// 类成员
}
// 泛型方法的约束语法
public 返回类型 方法名<T>(T 参数) where T : 约束条件
{
// 方法逻辑
}
限定泛型必须是类(class约束)
如果需要限定泛型参数必须是引用类型(也就是类、接口、委托、数组等,不过class约束通常用来限定为类类型),可以使用class约束。
class约束的使用示例
下面是一个泛型类的示例,要求泛型参数T必须是引用类型:
// 定义一个基类
public class BaseEntity
{
public int Id { get; set; }
}
// 泛型仓储类,约束T必须是引用类型
public class Repository<T> where T : class
{
// 模拟添加数据的方法
public void Add(T entity)
{
Console.WriteLine($"添加实体:{entity.GetType().Name}");
}
}
// 定义一个继承自BaseEntity的实体类
public class User : BaseEntity
{
public string UserName { get; set; }
}
class Program
{
static void Main()
{
// 合法:User是类,满足class约束
Repository<User> userRepo = new Repository<User>();
userRepo.Add(new User { Id = 1, UserName = "test" });
// 编译报错:int是值类型,不满足class约束
// Repository<int> intRepo = new Repository<int>();
}
}
如果尝试用值类型作为泛型参数传入带有class约束的泛型类或方法,编译器会直接报错,避免运行时出现类型不匹配的问题。
class约束的注意事项
- class约束必须放在所有约束的最前面,如果有多个约束的话
- class约束不能和
struct约束同时使用,因为一个类型不能既是引用类型又是值类型 - 满足class约束的类型包括自定义类、系统内置的引用类型如
string、object等
限定泛型必须是接口(interface约束)
如果需要限定泛型参数必须实现某个接口,或者必须是接口类型本身,可以使用接口作为约束条件,直接写接口的名称即可。
interface约束的使用示例
首先定义一个接口,然后约束泛型参数必须实现该接口:
// 定义一个数据验证接口
public interface IValidatable
{
bool Validate();
}
// 定义一个实现IValidatable接口的类
public class Order : IValidatable
{
public int OrderId { get; set; }
public int Count { get; set; }
public bool Validate()
{
return Count > 0;
}
}
// 泛型方法,约束T必须实现IValidatable接口
public static class Validator
{
public static bool Check<T>(T obj) where T : IValidatable
{
return obj.Validate();
}
}
class Program
{
static void Main()
{
Order order = new Order { OrderId = 1, Count = 5 };
// 合法:Order实现了IValidatable接口,满足约束
bool result = Validator.Check(order);
Console.WriteLine($"验证结果:{result}");
// 编译报错:string没有实现IValidatable接口,不满足约束
// bool strResult = Validator.Check("test");
}
}
通过接口约束,我们可以在泛型内部直接调用接口定义的方法,不需要额外的类型转换,提升了代码的安全性和可读性。
多个接口约束的情况
如果一个泛型参数需要满足多个接口的约束,可以用逗号分隔多个接口名称:
// 定义第二个接口
public interface IHasId
{
int Id { get; set; }
}
// 泛型类约束T必须同时实现IValidatable和IHasId接口
public class Processor<T> where T : IValidatable, IHasId
{
public void Process(T obj)
{
if (obj.Validate())
{
Console.WriteLine($"处理Id为{obj.Id}的对象");
}
}
}
// 定义同时实现两个接口的类
public class Product : IValidatable, IHasId
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public bool Validate()
{
return !string.IsNullOrEmpty(Name) && Price > 0;
}
}
class Program
{
static void Main()
{
Product product = new Product { Id = 1, Name = "手机", Price = 1999 };
Processor<Product> processor = new Processor<Product>();
processor.Process(product);
}
}
组合使用class约束和interface约束
如果需要限定泛型参数既是类,又实现了某个接口,可以把class约束放在最前面,后面跟接口约束:
// 泛型类约束T必须是类,并且实现IValidatable接口
public class Service<T> where T : class, IValidatable
{
public void Execute(T obj)
{
if (obj.Validate())
{
Console.WriteLine($"执行{obj.GetType().Name}的服务逻辑");
}
}
}
class Program
{
static void Main()
{
// 合法:Order是类且实现了IValidatable
Service<Order> orderService = new Service<Order>();
orderService.Execute(new Order { OrderId = 1, Count = 10 });
// 编译报错:接口不满足class约束
// Service<IValidatable> interfaceService = new Service<IValidatable>();
}
}
常见使用场景
| 约束类型 | 适用场景 |
|---|---|
| class约束 | 需要操作引用类型的成员,比如调用类的实例方法、访问引用类型的属性,或者需要支持null值的场景 |
| interface约束 | 需要泛型参数具备某些统一的行为,比如所有参数都需要实现序列化、验证、比较等功能时 |
| class+interface组合约束 | 需要泛型参数是具体的类类型,同时具备某些接口定义的能力,比如实体类仓储、业务处理器等场景 |
注意事项总结
- 泛型约束是在编译阶段生效的,不会增加运行时的性能开销
- 如果有多个泛型参数,每个参数都可以单独添加where约束
- 约束条件不能循环依赖,比如不能约束T1继承T2,同时约束T2继承T1
- 如果泛型参数没有约束,只能调用
object类定义的方法,添加约束后可以调用约束类型定义的成员
泛型约束是C#泛型编程中提升代码健壮性和可读性的重要特性,合理使用class和interface约束可以有效减少类型相关的错误,让代码的逻辑更清晰。
C#泛型约束class约束interface约束where关键字修改时间:2026-06-14 21:30:45