[C#2] 1-泛型
1. 泛型概述
泛型是一种类型的多态;比如当我们写一个栈或者队列的时候,需要指定其数据类型,int一份代码,string一份代码,object的一份代码, 这些代码除了数据类型不同之外其他大部分都是相同的,根据设计模式的思想,抽象出来变化点封装它, 共同的部分作为共用的代码。这里的变化点就是类型了,共同部分就是算法相同,所以就把类型抽象化, 于是乎泛型问世&[个人理解]。
C#泛型由CLR在运行时支持,这使得泛型可以在CLR支持的各种语言上无缝集合; C#泛型代码在被编译[第一次编译]为IL代码和元数据时[泛型版的IL和元数据], 采用特殊的占位符来表示泛型类型,并用专有的IL指令支持泛型操作,真正的泛型实例化工作发生在JIT编译[第二次编译]时。 当JIT编译器第一次遇到这种特殊的IL和元数据时,会利用实际的类型进行替换[泛型类型的实例化]。 CLR为所有类型参数是引用类型的泛型类型产生同一份代码,而对值类型来说,不同的值类型产生不同的代码, 相同的则共用同一份代码。
C#泛型类型携带有丰富的元数据,因此C#的泛型类型可以应用于强大的反射技术;采用[基类, 接口, 构造器, 值类型/引用类型]的约束方式来实现对类型参数的"显式约束", 提高了类型的安全性。DEMO:
public class MyType<T> where T : struct
{
private T[] _items;
public void Add(T itme)
{
}
}
编译后IL如下:
//泛型类<'1代表元数或者参数数量>
.class public auto ansi beforefieldinit MyType`1<valuetype .ctor
//注意这里加上了泛型约束<[mscorlib]System.ValueType) T>
//表明类型参数是值类型的
([mscorlib]System.ValueType) T>
extends [mscorlib]System.Object
{
} // end of class MyType`1
//这是那个私有字段
.field private !T[] _items
//Add方法,类型参数<T>之前有一个感叹号<!>,这是CIL开始支持泛型
//后引入的新特性,它指出为类指定的第一个类型参数的存在,表明这是
//一个类型参数
.method public hidebysig instance void Add(!T itme) cil managed
{
// 代码大小 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method MyType`1::Add
除了这些区别外,泛型类和非泛型类的CIL代码并无太大区别。
2. 泛型类型和泛型方法
可以用于泛型的类型有类、接口,结构、委托。
C#支持泛型方法,但不支持除方法外的其他成员[属性、事件、索引器、构造器、析构器。但这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数]; 泛型方法可以包含在泛型类型中,也可以包含在非泛型类型中[即普通的类型]。泛型方法:
public class GenericsMehod
{
//非泛型类中的泛型方法,参数约束为引用类型
//<传入值类型参数将引起编译错误>
public int FindItem<T>(T[] items, T item)where T:class
{
for (int i = 0; i < items.Length; i++)
{
if (items[i].Equals(item))
{
return i;
}
}
return -1;
}
}
调用就不写了,泛型方法支持重载,但是之区别类型参数约束的重载是非法的; 也是支持重写的,重写时的类型参数的约束被默认继承,任何的约束的指定都是不必要的,也是不可以指定约束的。
3. 泛型约束
为什么要有约束呢?假如我写了一个泛型类,这个泛型参数调用到CompareTo方法, 但是并不是所有的类型参数都有这个方法,假如传入的类型没这个方法,就会引起错误了, 所以保证你的代码的健壮的话,加上约束还是很有必要的[就是说传入的类型必须有这个方法才可以编译通过, 把错误暴露在编译阶段]。泛型约束支持四种形式的约束【接口约束,基类约束,构造器约束,值类型/引用类型约束】; 约束并不是必须的,如果没有指定约束,那么类型参数将只能访问System.Object类型中的公有方法。语法为where语句
上面的类型参数需要一个CompareTo方法就可以用一个接口约束加以实现:
public class MyGenerics<T>; where T : IComparable{}
基类约束:表是类型参数必须是继承子指定的类型<where T : 基类>;
构造器约束:只支持无参的构造器约束,就是必须保障参数类型可以调用它的无参构造器<where T : new()>:
值类型/引用类型约束:只有两种情况了<where T:struct>或者<where T:class>,指定参数类型必须是值类型或者引用类型;
- golang 函数定义及其接口实例
- 分享两种圣诞节雪花特效JS代码(网站下雪效果)
- React 移动 web 极致优化
- golang 高效字符串拼接
- Linux+Nginx/Apache/Tomcat新增SSL证书,开启https访问教程
- golang 使用时间通过md5生成token
- golang中对map操作类
- Nginx在线服务状态下平滑升级或新增模块的详细操作记录
- 【Dev Club分享】微信读书iOS性能优化
- [svn: E155004]svn update报database is locked错误的解决办法
- WordPress高亮插件:Crayon Syntax Highlighter加载优化
- 深入理解 ButterKnife,让你的程序学会写代码
- JS+CSS让网站嗨起来,博客要被玩坏了!
- Linux系统zip压缩命令详细参数,附文件排除选项的正确用法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 10 个你可能还不知道 VS Code 使用技巧
- Flutter 步骤进度组件
- 慌:一次订单号重复,差点被开除
- Flutter 分页功能表格控件
- 3分钟短文:Laravel 从软删除说到模型作用域的概念
- Flutter 系统是如何实现ExpansionPanelList的
- Flutter 粘合剂CustomScrollView控件
- Flutter 1.17版本重磅发布
- Flutter 首页必用组件NestedScrollView
- 【Flutter实战】文本组件及五大案例
- 你真的会用Flutter日期类组件吗
- 【Flutter实战】图片组件及四大案例
- Flutter 标签类控件大全Chip
- 【Flutter实战】六大布局组件
- 【Flutter实战】定位装饰权重组件及柱状图案例