Files

427 lines
7.4 KiB
Markdown
Raw Permalink Normal View History

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
#进阶