C#之使类型参数--泛型
1、泛型是什么
泛型的就是“通用类型”,它可以代替任何的数据类型,使类型参数化,从而达到只实现一个方法就可以操作多种数据类型的目的。
2、为什么使用泛型
举一个比较两个数大小的例子:
以上例子实现int类型数据的大小比较是完全没有问题的,但是如果客户现在增加需求“又可以实现两个字符串大小的比较”,此时就不得不在类中再添加一个比较字符串大小的方法了:
如果客户现在还增加需求,要求实现浮点型的比较,那么工作量就更大了,不得不再次修改代码,显然这不是我们想看到的,两个方法中有大部分代码是类似的,所以微软提出了一个激动人心的特性--泛型,他使得类型可以被参数化。
where语句是类型参数的约束它用来使参数可以适用于compareto方法。
向泛型中加入元素的效率远比非泛型数组高,原因是非泛型rraylist的add(object value)方法中,参数为object类型,当把int参数i传入方法时,会发生装箱操作,从而导致性能的损失,使运行的时间变得更长。
泛型可以保证类型安全,当你向int类型数组中添加string类型的值的时候,会造成“无法从string类型转换为int类型”的错误,因为你用int类型初始化了泛型类型。
3、泛型参数解析
1、类型参数
根据泛型类型参数是否已经提供实际类型,可分为未绑定的泛型和已构造的泛型,如果没有给泛型提供实际类型,此时的泛型成为未绑定的泛型;如果已指定了实际类型作为参数,此时的泛型成为已构造泛型。
已构造泛型又称为开放泛型和密封泛型。开放泛型指包含类型参数的泛型,所有未绑定的类型都属于开放类型;而封闭类型指已经为每个参数都指定了实际数据类型的泛型。
2、泛型中的静态字段和静态函数问题
对于非泛型类,定义了一个静态字段,不管是创建了多少个该类的实例,也不管从该类派生出多少个实例,都只存在一个字段,但是每个封闭的泛型类型中都有仅属于他自己的静态字段。
这是因为,在使用实际类型参数代替泛型参数时,编译器会根据不同的类型参数,从新生成类型。
对于静态构造函数,道理也是如此,每个封闭的泛型类型都有一个静态构造函数,这里就不一一演示。
3、类型参数的推断
写泛型的时候可以省略掉“<>”,具体实际类型交由编译器自行推断。
编译器会根据传入实参的类型判断实际类型参数。
4、类型参数的约束
(1)引用类型约束
表示形式:t:class;
以上代码中,where t:stream告诉编译器:传入的类型实参必须是system.io.stream,或者是从stream派生出的一个类型。
如果一个类型参数没有指定约束,那么默认t为system.object类型。但若在代码中显式指定了system.object约束,则编译器会报错:约束不能是特殊类object。
(2)值类型约束
表现形式:t:struct
它确保传递的类型实参是值类型(包含枚举),但这里的值类型不包括可空类型。
所有的值类型都有一个公共的无参构造函数,因此new t()是没有问题的,但是引用类型没有公共的无参构造函数,如果不对t进行约束,或约束为引用类型,则上边代码就会报错。
(3)构造函数类型约束
表现形式:t:new();
如果类型参数有多个约束,则次约束必须最后指定,构造函数类型约束确保制定的类型有一个公共无参的构造函数的非抽象类型。
(4)转换类型约束
表现形式:t:基类名、t:接口名或t:u
t:基类名确保制定的类型实参必须是基类或派生自基类的子类;
t:接口名确保制定的类型实参必须会接口或已经实现接口的类;
t:u确保t提供的是后面的类型实参或后面类型实参子类;
(5)组合约束
不同种类的约束合并到一起的约束,这里需要注意,没有任何一种类型既是引用类型,又是值类型,这两种约束不能同时使用。如果同时存在多个转换类型约束,其中有一个是类,那么类必须放在接口前面。不同的类型参数有不同的约束,但是每种类型参数必须使用单独的where关键字分开:
当然,c#中泛型的知识还有很多的应用场景,有兴趣的可以去找一些书籍看看,收获肯定不少,这里介绍只是让大家有一个比较直观的认识,看完后能够对泛型有一个比较直观的了解,懂得它有什么用,以及怎么去使用就行,更加高级的应用场景往后会慢慢分享,谢谢大家支持。