-
泛型约束——限制类型参数
泛型约束——限制类型参数
-
泛型约束的核心作用
泛型的灵活性让代码适配任意类型,但无限制的泛型可能导致类型不安全(比如调用不存在的方法)或性能损耗(比如频繁装箱拆箱)。泛型约束通过where关键字限制类型参数的合法范围,确保:
代码能安全调用类型参数的特定方法/属性;
避免不必要的类型转换;
提高编译时类型检查的严格性。 -
约束的语法与常用类型
泛型约束的语法为:where 类型参数 : 约束条件。多个约束用逗号分隔,new()约束必须放在最后。
2.1 常用约束类型
| 约束语法 | 作用说明 | 示例代码 |
|---|---|---|
| where T : class | T必须是引用类型(如string、自定义类、接口实例) |
public class Repository |
| where T : struct |
T必须是值类型(如int、double、DateTime,不包括Nullable |
public class Calculator |
| where T : new() | T必须有无参默认构造函数(允许创建T的实例) |
public class Factory |
| where T : BaseClass | T必须继承自BaseClass(可调用BaseClass的方法/属性) |
public class AnimalShelter |
| where T : IInterface | T必须实现IInterface(可调用接口的方法/属性) |
public class Sorter |
| where T : U | T必须是U的子类或同一类型(泛型类型之间的约束) | public class Container<T, U> where T : U |
-
实例:带约束的泛型数据存储类
我们以泛型仓储类为例,展示约束的实际应用。该类要求类型参数T满足:
是引用类型(class);
有默认构造函数(new());
实现IEntity接口(包含Id属性)。
3.1 完整代码实现
csharp
using System;
using System.Collections.Generic;
// 1. 定义接口:规范实体类必须有Id属性
public interface IEntity
{
int Id { get; set; }
}
// 2. 自定义实体类:实现IEntity接口
public class User : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
// 无参构造函数(满足new()约束)
public User() {}
}
// 3. 泛型仓储类:带多重约束
public class GenericRepository<T> where T : class, IEntity, new()
{
private List<T> _data = new List<T>();
// 添加实体:依赖IEntity的Id属性
public void Add(T entity)
{
if (entity.Id == 0) throw new ArgumentException("Id不能为空");
_data.Add(entity);
Console.WriteLine($"添加实体成功:Id={entity.Id}, Type={typeof(T).Name}");
}
// 根据Id查询实体:依赖IEntity的Id属性
public T GetById(int id)
{
return _data.Find(e => e.Id == id);
}
// 创建新实体:依赖new()约束
public T CreateNew()
{
return new T(); // 只有T有默认构造函数时才能调用
}
}
// 4. 测试代码
class Program
{
static void Main()
{
var repo = new GenericRepository<User>();
// 创建新实体(依赖new()约束)
var user = repo.CreateNew();
user.Id = 1;
user.Name = "张三";
user.Age = 25;
// 添加实体(依赖IEntity的Id属性)
repo.Add(user);
// 查询实体
var foundUser = repo.GetById(1);
Console.WriteLine($"查询结果:Name={foundUser.Name}, Age={foundUser.Age}");
}
}
3.2 逐行代码讲解
IEntity接口:定义Id属性,规范实体类的基本结构;
User类:实现IEntity接口,提供无参构造函数(满足new()约束);
GenericRepository
oclass:确保T是引用类型(避免值类型的装箱操作);
oIEntity:确保T有Id属性(允许调用entity.Id);
onew():允许通过new T()创建实例;
Add方法:检查Id是否为空(依赖IEntity),添加实体到列表;
CreateNew方法:直接创建T的实例(依赖new()约束);
测试逻辑:创建仓储→生成新用户→添加→查询,验证约束的有效性。
输出结果:
添加实体成功:Id=1, Type=User
查询结果:Name=张三, Age=25
-
基础知识拓展
4.1 约束的顺序规则
当使用多重约束时,需遵循以下顺序:
1.引用类型约束(class)或值类型约束(struct);
2.基类约束(BaseClass);
3.接口约束(IInterface);
4.泛型类型约束(U);
5.new()约束(必须放在最后)。
错误示例:where T : new(), class(new()在前,编译报错);
正确示例:where T : class, IEntity, new()。
4.2 无约束的泛型
若未指定约束,类型参数默认隐式约束为object,只能调用object的方法(如ToString()、Equals())。例如:
csharp
public class Unconstrained<T>
{
public void Print(T item)
{
Console.WriteLine(item.ToString()); // 只能调用object的方法
}
}
缺点:无法调用T的特定方法,需强制类型转换(风险高)。
4.3 约束的好处
类型安全:编译时检查类型参数是否符合要求,避免运行时错误;
性能优化:减少装箱拆箱操作(如struct约束避免值类型转object);
代码可读性:约束明确告诉开发者类型参数的预期用途;
功能扩展:允许调用约束类型的方法(如IComparable
5. 总结
泛型约束是泛型编程的“安全网”,通过限制类型参vb.net教程C#教程python教程SQL教程access 2010教程数的范围,让泛型代码既灵活又可靠。核心要点:
用where关键字定义约束;
常用约束包括class、struct、new()、基类/接口约束;
多重约束需遵循顺序规则;
约束能提升类型安全、性能和代码可读性。
6. 练习
实现一个泛型排序类GenericSorter
1.T必须实现IComparable
2.提供Sort(List
3.测试代码用int、string和自定义类(如Product,实现IComparable
提示:排序可使用冒泡排序或Array.Sort方法(依赖IComparable
(本节完)
下一章:泛型进阶——协变与逆变(深入泛型的类型转换特性)。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49373.html










