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