2026-06-22 22:11:42 +08:00
# 泛型约束
概念:
泛型默认可以接收很多类型
如果我们希望 `T` 只能是“某一类类型”,就要加==泛型约束==
作用:
1. 限制泛型参数的取值范围
2. 让编译器提前检查类型是否符合要求
3. 让我们可以安全地使用某些成员或写法
4. 比如 `new T()` 、调用接口方法、访问父类成员
关键字:
```csharp
where
```
----
# 通常用法
1. 限制泛型必须是引用类型或值类型
2. 限制泛型必须继承某个类
3. 限制泛型必须实现某个接口
4. 限制泛型必须有无参构造函数
5. 在工具类、工厂类、集合封装、通用算法中很常见
简单理解:
泛型像“留空的类型位置”
约束就是给这个空位加规则
不是任何类型都能随便传进来
----
# 语法
类上的泛型约束:
```csharp
class 类名 < T > where T : 约束
{
}
```
方法上的泛型约束:
```csharp
void 方法名 < T >() where T : 约束
{
}
```
多个泛型参数分别约束:
```csharp
class Test < T , K >
where T : class
where K : new ()
{
}
```
多个约束组合写法:
```csharp
class Test < T > where T : 父类名 , 接口名 , new ()
{
}
```
----
# 常见约束
常见的基础约束有 6 种:
| 约束写法 | 含义 |
|---|---|
| `where T : struct` | `T` 必须是值类型 |
| `where T : class` | `T` 必须是引用类型 |
| `where T : new()` | `T` 必须有公共无参构造函数 |
| `where T : 类名` | `T` 必须是某个类或它的子类 |
| `where T : 接口名` | `T` 必须实现某个接口 |
| `where T : U` | `T` 必须是另一个泛型参数 `U` 或它的派生类型 |
补充:
现在 C# 里还会见到一些新约束,比如:
`class?` 、`notnull` 、`unmanaged` 、`default`
入门和面试里最常见的,还是前面这 6 种
----
# 1. struct 约束
作用:
要求泛型参数必须是==值类型==
```csharp
class Test < T > where T : struct
{
}
```
正确:
```csharp
Test < int > t1 = new Test < int >();
Test < bool > t2 = new Test < bool >();
```
错误:
```csharp
// Test<string> t3 = new Test<string>();
```
说明:
`int` 、`float` 、`bool` 、`char` 、结构体 都属于值类型
----
# 2. class 约束
作用:
要求泛型参数必须是==引用类型==
```csharp
class Test < T > where T : class
{
}
```
正确:
```csharp
Test < string > t1 = new Test < string >();
Test < object > t2 = new Test < object >();
```
错误:
```csharp
// Test<int> t3 = new Test<int>();
```
说明:
类、数组、字符串、委托等通常都是引用类型
----
# 3. new() 约束
作用:
要求泛型参数必须有==公共无参构造函数==
```csharp
class Factory < T > where T : new ()
{
public T Create ()
{
return new T ();
}
}
```
案例:
```csharp
class Player
{
public Player ()
{
}
}
Factory < Player > factory = new Factory < Player >();
Player p = factory . Create ();
```
说明:
如果不加 `where T : new()` ,就不能直接写 `new T()`
----
# 4. 父类约束
作用:
要求泛型参数必须是==某个类本身或它的子类==
```csharp
class Animal
{
public void Eat ()
{
Console . WriteLine ( "吃东西" );
}
}
class Dog : Animal
{
}
class Test < T > where T : Animal
{
public void DoSomething ( T value )
{
value . Eat ();
}
}
```
使用:
```csharp
Test < Animal > t1 = new Test < Animal >();
Test < Dog > t2 = new Test < Dog >();
```
说明:
因为 `T` 一定是 `Animal` 或其子类
所以可以直接把 `T` 当成 `Animal` 来用
----
# 5. 接口约束
作用:
要求泛型参数必须==实现某个接口==
```csharp
interface IFly
{
void Fly ();
}
class Bird : IFly
{
public void Fly ()
{
Console . WriteLine ( "飞行" );
}
}
class Test < T > where T : IFly
{
public void StartFly ( T value )
{
value . Fly ();
}
}
```
使用:
```csharp
Test < Bird > t = new Test < Bird >();
t . StartFly ( new Bird ());
```
说明:
因为约束保证了 `T` 一定有 `Fly()`
所以在泛型里可以直接调用接口成员
----
# 6. 泛型参数之间的约束
作用:
让一个泛型参数受另一个泛型参数限制
```csharp
class Test < T , K > where T : K
{
}
```
理解:
`T : K` 表示 `T` 必须是 `K` 本身
或者是 `K` 的派生类型、实现类型
案例:
```csharp
class Animal
{
}
class Dog : Animal
{
}
Test < Dog , Animal > t = new Test < Dog , Animal >();
```
----
# 组合约束
泛型约束可以组合使用:
```csharp
class Test < T > where T : Animal , IFly , new ()
{
public T CreateAndUse ()
{
T value = new T ();
value . Eat ();
value . Fly ();
return value ;
}
}
```
含义:
1. `T` 必须继承 `Animal`
2. `T` 必须实现 `IFly`
3. `T` 必须有公共无参构造函数
----
# 基础案例
```csharp
using System ;
interface IRun
{
void Run ();
}
class Player : IRun
{
public Player ()
{
}
public void Run ()
{
Console . WriteLine ( "玩家正在移动" );
}
}
class Manager < T > where T : IRun , new ()
{
public T Create ()
{
return new T ();
}
public void Start ( T obj )
{
obj . Run ();
}
}
Manager < Player > manager = new Manager < Player >();
Player player = manager . Create ();
manager . Start ( player );
```
输出:
```text
玩家正在移动
```
理解:
因为 `T` 同时满足 `IRun` 和 `new()`
所以既能 `new T()` ,又能调用 `Run()`
----
# 约束能解决什么问题
没有约束时:
```csharp
class Test < T >
{
public void Show ( T value )
{
// 这里不能随便调用 value 的成员
}
}
```
原因:
编译器不知道 `T` 到底是什么类型
加上约束后:
```csharp
interface IShow
{
void Show ();
}
class Test < T > where T : IShow
{
public void Print ( T value )
{
value . Show ();
}
}
```
说明:
约束的本质,就是告诉编译器:
“这个泛型参数至少具备这些能力”
----
# 注意
1. 泛型约束使用关键字 `where`
2. `new()` 约束表示必须有公共无参构造函数
3. 想写 `new T()` ,必须加 `where T : new()`
4. 父类约束和接口约束可以让我们安全调用成员
5. 一个泛型参数可以同时有多个约束
6. 如果有 `new()` ,通常要写在约束列表最后
7. `struct` 和 `class` 一般不能同时使用
8. 约束的作用是“限制类型 + 提供可用能力”
----
# 易错点
1. `new()` 不是“可以 new 一切”,而是“必须存在公共无参构造函数”
2. 有参构造函数不算满足 `new()`
3. `where T : 类名` 表示继承这个类,不是“名字像这个类”
4. `where T : 接口名` 表示实现接口,不是普通包含同名方法
5. 没有约束时,不能因为自己“知道”类型就直接调用成员
错误示例:
```csharp
class Person
{
public Person ( string name )
{
}
}
// Factory<Person> factory = new Factory<Person>();
```
原因:
`Person` 没有公共无参构造函数
所以不满足 `new()`
----
# 一句话记忆
==泛型约束就是给泛型参数加条件,让它只能使用满足要求的类型。==
----
# 面试/复习时怎么说
可以这样答:
“泛型约束就是用 `where` 给泛型参数加限制。
比如可以限制它必须是值类型、引用类型、某个父类、某个接口,或者必须有无参构造函数。
这样做的好处是,编译器能提前帮我们检查类型,同时我们在泛型内部也能更安全地 `new T()` 或调用指定成员。”
----
引用:
1. Microsoft Learn: [Constraints on type parameters ](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters )
2. Microsoft Learn: [where (generic type constraint) ](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint )
#泛型约束
#泛型
#CSharp
#进阶