C# 泛型概念及用法详解
C# 泛型概念及用法详解
一,定义
泛型是 C# 的一种语言特性,允许在定义类、接口、方法、委托时,不指定具体的数据类型,而是用一个类型参数(Type Parameter)来代替,等到使用时再指定具体类型。这会大大简化我们的代码结构,同时让后期维护变得容易。
废话一大堆,那么泛型到底长啥样?,看到下面代码中的 “T” 没有,那就是泛型。
static void Main(string[] args) { Program program = new Program(); program.MyClassMethod(100); program.MyClassMethod2<int>(100); } public void MyClassMethod(int a) { Console.WriteLine(a); } public void MyClassMethod2<T>(T a) { Console.WriteLine(a); Console.WriteLine(a.GetType().ToString()); }二,为什么要用泛型
重载是 “每种类型写一遍”,泛型是 “写一遍所有类型通用”;类型少的时候重载还行,类型多了泛型才是正道 —— 代码复用、维护方便、类型安全。
namespace 泛型 { class Program { static void Main(string[] args) { int result = Test.Add(3, 4); Console.WriteLine(result); Console.ReadKey(); } } public class Test { public static int Add(int x, int y) { return x + y; } public static int Add(string x, string y) { return int.Parse(x) + int.Parse(y); } } }那么下面就用泛型的方式来封装一下这个加法运算。
注意一下写法,在 Add 后面要加上 这句,不然会报错。
public int Add<T>(T x, T y) { return 0 ; }三、泛型和 Object 类型的区别
下面是一些介绍:
C# 中Object是一切类型的基类,可以用来表示所有类型,而泛型是指将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。一句话总结 object 是 “万能盒子”,什么都能装,但装进去就不知道是什么了,用的时候要小心;泛型是 “模板盒子”,你指定装什么它就装什么,编译器帮你盯着,不会装错。
C# 中泛型是 “通用又安全”,一份代码多种类型能用,编译器帮你检查类型,不用装箱拆箱性能还好。
**object缺点:**1.因为什么都能装,编译器不检查,写错了也不报错,运行时才崩。
2.值类型(int、double、结构体)装到 object 里,要复制到堆上;取出来又要复制回 栈上,频繁装箱拆箱导致性能损耗。
object优点:1. object类型可以用来引用任何类型的实例;
2. object类型可以存储任何类型的值;
3.可以定义object类型的参数; 4.可以把object作为返回类型。
四、泛型类
定义
整个类都是泛型的,类里的字段、方法都可以用类型参数 T。
public class MyList<T> { private List<T> _list = new List<T>(); public int Count => _list.Count; public void Add(T item) { if (item != null) _list.Add(item); } } // 使用 MyList<int> intList = new MyList<int>(); intList.Add(1); intList.Add(3); Console.WriteLine(intList.Count); // 2你天天用的
List就是泛型类。
五,泛型类的继承
// 方式1:子类也是泛型,T往下传 public class Test1<T> { } public class Test2<T> : Test1<T> { } // 方式2:子类指定父类的具体类型 public class Test1<T> { } public class Test2<T> : Test1<int> { } // 父类T固定为int六、泛型方法
**定义:**只有方法是泛型的,类不一定是泛型的。
public class Tool { // 泛型方法:只有这个方法用T public static T GetT<T>(T a) { return a; } }七、泛型接口
**定义:**接口也是泛型的。
public interface IFace<T> { void SayHi(T msg); }两种实现方式
// 方式1:普通类实现,指定具体类型 public class MyClass1 : IFace<string> { public void SayHi(string msg) { Console.WriteLine(msg); } } // 方式2:泛型类实现,T继续往下传 public class MyClass2<T> : IFace<T> { public void SayHi(T msg) { Console.WriteLine(msg); } }八、泛型约束(重点!)
什么是泛型约束?
给类型参数 T 定规矩,告诉编译器 T 必须满足什么条件,这样方法里才能放心调用 T 的成员。
六种约束
| 约束 | 写法 | 含义 |
|---|---|---|
| 值类型约束 | where T : struct | T 必须是值类型 |
| 引用类型约束 | where T : class | T 必须是引用类型 |
| 无参构造约束 | where T : new() | T 必须有无参构造函数 |
| 基类约束 | where T : 基类名 | T 必须是该基类或其子类 |
| 接口约束 | where T : 接口名 | T 必须实现该接口 |
| 裸类型约束 | where T : U | T 必须是 U 或 U 的子类 |
九,约束顺序
基类 → 接口 → new()基类放最前面,接口放中间,new()放最后。
多个约束示例
public class MyClass<T, K, V, W, X, Y> where T : struct // T必须是值类型 where K : class // K必须是引用类型 where V : IFace<T> // V必须实现IFace<T>接口 where W : K // W必须是K或K的子类 where X : class, new() // X必须是引用类型 + 有无参构造 where Y : MyClass2 // Y必须是MyClass2或其子类 { // ... }十、泛型 + 反射(进阶应用)
应用场景
通用比较两个对象的所有属性值是否相同,不用写一堆 if-else。
核心思路
用反射获取对象的所有属性,逐个比较,递归处理嵌套对象。
public static bool CompareType<T>(T oneT, T twoT) { Type type1 = oneT.GetType(); Type type2 = twoT.GetType(); if (!type1.Equals(type2)) return false; PropertyInfo[] props1 = type1.GetProperties(); PropertyInfo[] props2 = type2.GetProperties(); // 遍历所有属性比较 for (int i = 0; i < props1.Length; i++) { object val1 = props1[i].GetValue(oneT, null); object val2 = props2[i].GetValue(twoT, null); if (!val1.Equals(val2)) return false; } return true; }好处:写一个方法,所有类型的对象都能比,不用每种类型写一遍。
十一、一句话速记卡
- 泛型= 类型参数化,写一遍代码所有类型通用
- 泛型类= 整个类都是模板,List 就是
- 泛型方法= 只有方法是模板
- 泛型接口= 接口是模板,实现时指定类型
- 泛型委托= 委托参数是模板
- 泛型约束= 给 T 定规矩,struct/class/new ()/ 基类 / 接口
- vs Object= 泛型类型安全、不用装箱、不用强转
