Files
My-Notes/笔记/CSharp/进阶/泛型.md
T
2026-06-21 09:51:55 +08:00

381 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 泛型
概念:
泛型就是把==类型==参数化,让一份代码可以适配多种数据类型。
可以理解为:先把类型空出来,等使用时再指定具体类型。
例如:
```csharp
List<int> nums = new List<int>();
List<string> names = new List<string>();
```
其中 `int``string` 就是传入泛型的具体类型。
----
# 为什么需要泛型
泛型的主要作用:
1. 提高代码复用性
2. 保证类型安全
3. 减少强制类型转换
4. 避免不必要的装箱和拆箱
对比 `ArrayList`
```csharp
ArrayList list = new ArrayList();
list.Add(1);
list.Add("hello");
int num = (int)list[0];
```
`ArrayList` 内部存的是 `object`,取值时经常需要强制转换。
使用泛型集合:
```csharp
List<int> list = new List<int>();
list.Add(1);
int num = list[0];
```
`List<int>` 只能存 `int`,编译器会提前帮我们检查类型错误。
----
# 常见泛型形式
泛型可以用于:
1. 泛型类
2. 泛型接口
3. 泛型方法
4. 泛型委托
5. 泛型集合
常见泛型集合:
```csharp
List<T>
Dictionary<TKey, TValue>
Queue<T>
Stack<T>
HashSet<T>
```
`T``TKey``TValue` 都是泛型类型参数名。
----
# 语法
泛型类:
```csharp
class 类名<T>
{
}
```
泛型接口:
```csharp
interface 接口名<T>
{
}
```
泛型方法:
```csharp
返回值类型 方法名<T>(T 参数)
{
}
```
多个泛型类型参数:
```csharp
class 类名<T1, T2>
{
}
```
----
# 泛型类
泛型类适合用来保存或处理某种不确定类型的数据。
```csharp
class Box<T>
{
public T Value { get; set; }
public Box(T value)
{
Value = value;
}
public T GetValue()
{
return Value;
}
}
```
使用:
```csharp
Box<int> intBox = new Box<int>(10);
Console.WriteLine(intBox.GetValue());
Box<string> stringBox = new Box<string>("hello");
Console.WriteLine(stringBox.GetValue());
```
理解:
1. `Box<T>` 定义时不知道 `T` 是什么类型
2. `Box<int>` 使用时把 `T` 替换成 `int`
3. `Box<string>` 使用时把 `T` 替换成 `string`
----
# 泛型接口
泛型接口常用于规定一类对象必须提供某种泛型能力。
```csharp
interface IRepository<T>
{
void Add(T item);
T Get(int index);
}
```
实现:
```csharp
class MemoryRepository<T> : IRepository<T>
{
private List<T> items = new List<T>();
public void Add(T item)
{
items.Add(item);
}
public T Get(int index)
{
return items[index];
}
}
```
使用:
```csharp
IRepository<string> repository = new MemoryRepository<string>();
repository.Add("小明");
Console.WriteLine(repository.Get(0));
```
----
# 泛型方法
泛型方法适合处理“逻辑一样,但参数类型不同”的情况。
```csharp
static void Print<T>(T value)
{
Console.WriteLine(value);
}
```
使用:
```csharp
Print<int>(100);
Print<string>("hello");
Print<bool>(true);
```
很多时候,编译器可以自动推断泛型类型:
```csharp
Print(100);
Print("hello");
Print(true);
```
----
# 泛型约束
默认情况下,泛型类型 `T` 可以是任意类型。
如果希望限制 `T` 必须满足某些条件,可以使用 `where` 约束。
语法:
```csharp
class 类名<T> where T : 约束
{
}
```
常见约束:
| 约束 | 含义 |
| --- | --- |
| `where T : class` | `T` 必须是引用类型 |
| `where T : struct` | `T` 必须是值类型 |
| `where T : new()` | `T` 必须有无参构造函数 |
| `where T : 父类名` | `T` 必须继承某个父类 |
| `where T : 接口名` | `T` 必须实现某个接口 |
案例:
```csharp
class Factory<T> where T : new()
{
public T Create()
{
return new T();
}
}
```
因为加了 `where T : new()`,所以可以在类中使用 `new T()`
----
# 多泛型参数
一个类或方法可以同时使用多个泛型参数。
```csharp
class Pair<TKey, TValue>
{
public TKey Key { get; set; }
public TValue Value { get; set; }
public Pair(TKey key, TValue value)
{
Key = key;
Value = value;
}
}
```
使用:
```csharp
Pair<int, string> pair = new Pair<int, string>(1, "小明");
Console.WriteLine(pair.Key);
Console.WriteLine(pair.Value);
```
----
# default 关键字
泛型中有时不知道 `T` 的默认值是什么,可以使用 `default`
```csharp
static T GetDefault<T>()
{
return default(T);
}
```
常见默认值:
| 类型 | 默认值 |
| --- | --- |
| `int` | `0` |
| `bool` | `false` |
| 引用类型 | `null` |
| 结构体 | 所有字段都是默认值 |
新语法也可以写成:
```csharp
return default;
```
----
# 完整案例
```csharp
using System;
using System.Collections.Generic;
namespace GenericsDemo
{
internal class Program
{
static void Main(string[] args)
{
DataStore<int> intStore = new DataStore<int>();
intStore.Add(10);
intStore.Add(20);
Console.WriteLine(intStore.Get(0));
Console.WriteLine(intStore.Get(1));
DataStore<string> stringStore = new DataStore<string>();
stringStore.Add("hello");
stringStore.Add("world");
Console.WriteLine(stringStore.Get(0));
Console.WriteLine(stringStore.Get(1));
Print(123);
Print("泛型方法");
}
static void Print<T>(T value)
{
Console.WriteLine(value);
}
}
interface IDataStore<T>
{
void Add(T value);
T Get(int index);
}
class DataStore<T> : IDataStore<T>
{
private List<T> values = new List<T>();
public void Add(T value)
{
values.Add(value);
}
public T Get(int index)
{
return values[index];
}
}
}
```
理解:
1. `DataStore<T>` 是泛型类
2. `IDataStore<T>` 是泛型接口
3. `Print<T>` 是泛型方法
4. `DataStore<int>` 只能保存整数
5. `DataStore<string>` 只能保存字符串
----
# 注意
1. 泛型类型参数常用 `T` 表示,但不是固定写法
2. 多个泛型参数常写成 `TKey``TValue``TItem`
3. `List<T>``ArrayList` 更常用,也更安全
4. 泛型在编译期就能检查类型错误
5. 使用泛型可以减少 `object` 带来的强制转换
6. 如果需要在泛型中 `new T()`,必须加 `where T : new()` 约束
7. 泛型不是“任何类型都乱放”,而是“使用时确定一种类型”
----
# 小结
一句话:
泛型就是==把类型当作参数传进去==。
记忆:
普通参数控制“值”。
泛型参数控制“类型”。
最常见写法:
```csharp
List<int> nums = new List<int>();
Dictionary<string, int> scores = new Dictionary<string, int>();
```