差别
这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
| csharp:类:泛型 [2025/09/28 16:37] – 张叶安 | csharp:类:泛型 [2025/11/27 17:25] (当前版本) – 张叶安 | ||
|---|---|---|---|
| 行 1: | 行 1: | ||
| - | # 概念 | + | ====== C# 泛型 (Generics) ====== |
| - | 使用在类与方法上、使用占位符T 来代表某种类型,编译期间决定其具体类型 | + | ===== 1. 概念 ===== |
| - | 类名称后面加 <T> 代表该类就是泛型类、不一定是T < | + | 泛型主要用于类与方法上,使用占位符(通常是 '' |
| - | 泛型在实例化时、或使用时占位符T会被特化成指定的类型 | + | * 类名称后面加 ''< |
| + | * 占位符不一定是 '' | ||
| + | * 泛型在实例化时(或使用时),占位符 | ||
| - | 为什么要加< | + | ==== 为什么要使用泛型? |
| - | <T>:是类型占位符、表示还没决定具体是什么类型、先把这个位置占了、 | + | '' |
| - | 如下例子: | + | **场景举例**: |
| + | 当不确定类里面是 '' | ||
| - | 当不确定类里面 是int[]类型数组 或者string[]类型数组 疑惑别的数组 | + | ===== 2. 泛型类 |
| - | # 泛型类: | + | 这是一个模拟栈(Stack)内存空间的泛型类示例。 |
| - | ``` | + | |
| - | class zhan< | + | |
| - | { // | + | |
| - | private T[] zhan_Cpu = new T[300] ; | + | |
| - | // | + | |
| - | public void set(T valu , int index) | + | |
| - | { | + | |
| - | zhan_Cpu[index] = valu; // | + | |
| - | } | + | |
| - | // | + | <code csharp> |
| - | | + | class zhan<T> |
| - | { | + | { |
| - | | + | |
| - | } | + | private T[] zhan_Cpu |
| - | } | + | |
| - | // | + | |
| - | static void Main(string[] args) | + | |
| - | { // | + | |
| - | zhan< | + | |
| - | // 此时int_data对象 内的数组zhan_Cpu读写都是int类型 | + | |
| - | zhan< | + | // 将数据存入栈的方法 |
| - | // 此时str_data对象 内的数组zhan_Cpu读写都是string类型 | + | |
| + | { | ||
| + | zhan_Cpu[index] = valu; // 数据入栈 | ||
| + | } | ||
| - | zhan< | + | // 将数据取出栈的方法 |
| - | // 此时doub_data对象 内的数组zhan_Cpu读写都是double类型 | + | |
| + | { | ||
| + | return zhan_Cpu[index]; | ||
| + | } | ||
| + | } | ||
| - | } | + | // 使用示例: |
| + | class Program | ||
| + | { | ||
| + | | ||
| + | | ||
| + | // 1. 对泛型类 zhan 的特化、特化为 int 类型的类 | ||
| + | zhan< | ||
| + | // 此时 int_data 对象内的数组 zhan_Cpu 读写都是 int 类型 | ||
| - | ``` | + | // 2. 特化为 string 类型 |
| + | zhan< | ||
| + | // 此时 str_data 对象内的数组 zhan_Cpu 读写都是 string 类型 | ||
| + | // 3. 特化为 double 类型 | ||
| + | zhan< | ||
| + | // 此时 doub_data 对象内的数组 zhan_Cpu 读写都是 double 类型 | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| - | # 泛型格式: | + | ===== 3. 泛型方法与 dynamic ===== |
| - | 格式无返回值: | + | ==== 格式:无返回值 |
| - | ``` | + | |
| - | void fangfa< | + | |
| - | { | + | |
| - | | + | <code csharp> |
| - | ``` | + | void fangfa< |
| + | { | ||
| + | // 方法体 | ||
| + | } | ||
| + | </ | ||
| - | 格式有返回值: | + | ==== 格式:有返回值与 dynamic ==== |
| - | ``` | + | |
| - | public | + | |
| - | { | + | |
| - | return a + b; // | + | |
| - | // | + | |
| - | dynamic da = a; // | + | 在泛型中直接进行算术运算(如 '' |
| - | dynamic db = b; // | + | |
| - | return da + db; | + | |
| - | } | + | |
| - | ``` | + | |
| - | dynamic | + | <code csharp> |
| + | public T fangfa1< | ||
| + | { | ||
| + | // return a + b; // 报错:运算符不能用于两个 T 类型的操作数 | ||
| + | // 原因:编译器会在编译时对参数 a, b 校验,无法确定 | ||
| - | daɪˈnæ mɪk | + | dynamic |
| + | dynamic db = b; | ||
| + | |||
| + | // 当运行时,已经为 T 类型赋予了 int 或者 double 类型,此时可以相加 | ||
| + | return da + db; | ||
| + | } | ||
| + | </ | ||
| - | # 多样化泛型 | + | > **dynamic (动态)** |
| - | ``` | + | * |
| + | * | ||
| + | |||
| + | ===== 4. 多样化泛型 ===== | ||
| + | |||
| + | 泛型可以定义多个占位符,用于处理多种不同类型的数据。 | ||
| + | |||
| + | <code csharp> | ||
| class Program | class Program | ||
| { | { | ||
| static void Main(string[] args) | static void Main(string[] args) | ||
| { | { | ||
| - | | + | // 实例化时,分别指定 T=int, U=string, F=double |
| - | Fanxing.LeiXing_1[1] = 500; | + | |
| - | | + | |
| - | Fanxing.LeiXing_3[1] = 63.588; | + | |
| + | | ||
| + | Fanxing.LeiXing_3[1] = 63.588; | ||
| + | } | ||
| } | } | ||
| + | |||
| + | // 多样化泛型类 | ||
| + | class reyy<T, U, F> | ||
| + | { | ||
| + | public T[] LeiXing_1 = new T[300]; | ||
| + | public U[] LeiXing_2 = new U[300]; | ||
| + | public F[] LeiXing_3 = new F[300]; | ||
| } | } | ||
| + | </ | ||
| - | //多样化泛型类 | + | ===== 5. 泛型的继承 ===== |
| - | class reyy<T , U , F> | + | |
| - | { | + | |
| - | public T[] LeiXing_1 | + | |
| - | public U[] LeiXing_2 | + | |
| - | public F[] LeiXing_3 | + | |
| - | } | + | |
| - | ``` | + | |
| - | # 泛型的继承-1普通类继承泛型 | + | 泛型类也可以被继承,主要分为两种情况。 |
| - | 泛型可以被继承 | + | ==== 情况 1:普通类继承泛型 ==== |
| - | 例如:继承时特化泛型类型 | + | |
| - | ``` | + | |
| - | class Program | + | |
| - | { | + | |
| - | static void Main(string[] args) | + | |
| - | { | + | |
| - | teacher Teacher | + | |
| - | Teacher.id | + | |
| - | } | + | |
| - | } | + | 在继承时,直接**特化**泛型类型(确定父类的类型)。 |
| - | class people<T> | + | |
| + | <code csharp> | ||
| + | class Program | ||
| + | { | ||
| + | static void Main(string[] args) | ||
| { | { | ||
| - | //编号属性 | + | |
| - | | + | |
| } | } | ||
| - | class teacher : people< | + | } |
| - | // | + | |
| - | { | + | |
| - | | + | |
| - | ``` | + | |
| - | # 泛型的继承-2泛型继承泛型 | + | class people< |
| + | { | ||
| + | // 编号属性 | ||
| + | public T id { get; set; } | ||
| + | } | ||
| - | 泛型继承泛型、当子类被实例化时特化的类型也会传导给父类 | + | // 继承一个泛型父类、继承的同时特化泛型为 int |
| - | ``` | + | class teacher |
| - | class Program | + | { |
| - | { | + | } |
| - | static void Main(string[] args) | + | </ |
| - | { | + | |
| - | | + | |
| - | // | + | |
| - | Teacher.id = 550346;// | + | |
| - | } | + | |
| - | } | + | |
| - | class people<T> | + | ==== 情况 2:泛型继承泛型 ==== |
| + | |||
| + | 子类也是泛型,当子类被实例化时,特化的类型会**传导**给父类。 | ||
| + | |||
| + | <code csharp> | ||
| + | class Program | ||
| + | { | ||
| + | static void Main(string[] args) | ||
| { | { | ||
| - | //编号属性 | + | // 实例化老师对象,特化为 int |
| - | | + | |
| + | teacher< | ||
| + | |||
| + | Teacher.id = 550346; // 老师作为人类的 id | ||
| } | } | ||
| - | class teacher< | + | } |
| - | { | + | |
| - | // | + | |
| - | // | + | |
| - | | + | |
| - | ``` | + | |
| - | # 总结 | + | |
| - | {{.: | + | class people< |
| + | { | ||
| + | // 编号属性 | ||
| + | public T id { get; set; } | ||
| + | } | ||
| + | // 当无法确定子类的类型时、可以把子类看做泛型类 | ||
| + | // 当子类实例化时、特化的类型 B 也会传导给父类 T | ||
| + | class teacher< | ||
| + | { | ||
| + | } | ||
| + | </ | ||
| + | ===== 6. 泛型约束 (Constraints) ===== | ||
| + | 默认情况下,泛型 '' | ||
| + | 这时就需要使用 **'' | ||
| + | ==== 常用约束列表 ==== | ||
| + | ^ 约束语法 ^ 说明 ^ | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | ==== 示例代码 ==== | ||
| + | |||
| + | <code csharp> | ||
| + | // 1. 接口约束示例 | ||
| + | // 只有实现了 IDBItem 接口的类,才能作为 T 传入 | ||
| + | public bool IsExist< | ||
| + | { | ||
| + | // 因为加了约束,编译器知道 T 一定有 IDBItem 的特性 | ||
| + | return true; | ||
| + | } | ||
| + | |||
| + | // 2. 构造函数约束示例 | ||
| + | // 只有包含无参构造函数的类,才能作为 T 传入 | ||
| + | class Factory< | ||
| + | { | ||
| + | public T CreateInstance() | ||
| + | { | ||
| + | // 如果没有 new() 约束,这里写 new T() 会报错 | ||
| + | return new T(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // 3. 组合约束 | ||
| + | // T 必须同时满足:是引用类型、实现了 IDisposable 接口、且有无参构造函数 | ||
| + | class MyManager< | ||
| + | { | ||
| + | } | ||
| + | </ | ||
| + | ===== 7. 总结 ===== | ||
| + | {{.: | ||