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