标签:不同 兼容 替换 优化 派生类 调试 2.3 指针 指令
目录:
12.1 FCL中的泛型
12.2 泛型基础结构
12.3 泛型接口
12.4 泛型委托
12.5 委托和接口的逆变和协变泛型类型实参
12.6 泛型方法
12.7 泛型和其他成员
12.8 可验证性和约束
泛型时CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用,即“算法重用”。
大多数算法允许都封装在一个类型中,CLR允许创建泛型引用类型和泛型值类型,但不允许创建泛型枚举类型。CLR还允许创建泛型接口和泛型委托。方法偶尔也封装有用的算法,所以CLR允许在引用类型,值类型或接口中定义泛型方法。
定义泛型类型或方法时,为类型指定的任何变量(比如T)都称为类型参数。T是变量名,源代码能使用数据类型的任何地方都能使用T。
使用泛型类型或方法时指定的具体数据类型称为类型实参。
优势:
源代码保护:使用泛型算法的开发人员不需要访问算法的源代码。
类型安全:将泛型算法应用于一个具体的类型时,编译器和CLR能理解开发人员的意图,并保证只有与指定数据类型兼容的对象才能用于算法。
更清晰的代码:由于编译器强制类型安全性,所以减少了源代码中必须进行的强制类型转换次数,使代码易于编写和维护。
更佳的性能:CLR不需要执行装箱操作,值类型的实例以传值的方式传递。
泛型最明显的应用就是集合类。集合类型实现了许多接口,放入集合中的对象可实现接口来执行排序和搜索等操作。
CLR添加泛型需要的工作:
创建新的IL指令,使之能够识别类型参数。
修改现有元数据表的格式,以便表示具有泛型参数的类型名称和方法。
修改各种编程语言(C#,Microsoft Visual Basic .NET等)来支持新语法,允许开发人员定义和引用泛型类型和方法。
修改编译器,使之能生成新的IL指令和修改的元数据格式。
修改JIT编译器,以便处理新的支持类型实参的IL指令来生成正确的本机代码。
创建新的反射成员,使开发人员能查询类型和成员,以判断它们是否具有泛型参数。另外,还必须定义新的反射成员,使开发人员能在运行时创建泛型类型和方法定义。
修改调试器以显示和操纵泛型类型,成员,字段以及局部变量。
修改VS的“智能感知”功能。将泛型类型或方法应用于特性数据类型时能显示成员的原型。
12.2.1 开放类型和封闭类型
具有泛型类型参数的类型成为开发类型,CLR禁止构造开放类型的任何实例。这类似于CLR禁止构造接口类型的实例。
代码引用泛型类型时可指定一组泛型类型实参。为所有类型参数都传递了实际的数据类型,类型就成为封闭类型。CLR允许构造封闭类型的实例。然而,代码引用泛型类型的时候,可能留下一些泛型类型实参未指定。这会在CLR中创建新的开发类型对象,而且不能创建该类型的实例。
12.2.2 泛型类型和继承
泛型类型仍然时类型,所以能从其他任何类型派生。使用泛型类型并指定类型实参时,实际是在CLR中定义一个新的类型对象,新的类型对象从泛型类型派生自的那个类型派生。
12.2.3 泛型类型同一性
12.2.4 代码爆炸
使用泛型类型参数的方法在进行JIT编译时,CLR获取方法的IL,用指定的类型实参替换,然后创建恰当的本机代码(这些代码未操作指定数据类型“量身定制”)。缺点:CLR要为每种不同的方法/类型组合生成本机代码。这个现象时“代码爆炸”。
CLR内建了一些优化措施能缓解代码爆炸。假如为特定的类型实参调用了一个方法,以后再用相同的类型实参调用这个方法,CLR只会为这个方法/类型组合编译一次代码。
CLR还有另一优化,它认为所有引用类型实参都完全相同,所以代码能够共享。(所有引用类型的参数或变量实际上只是指向堆上对象的指针,所有对象指针都以相同方式操纵)
如果某个类型时值类型,CLR就必须专门为那个值类型生成本机代码。因为值类型的大小不定,CLR无法共享代码,因为可能要用不同的本机CPU指令来操纵这些值。
使用非泛型接口来操纵值类型会发生装箱,而且会失去编译时的类型安全性。更到内容请参考在13章接口
CLR支持泛型委托,目的是保证任何类型的对象都能以类型安全的方式传给回调函数。泛型委托允许值类型实例在传给给回调方法时不进行任何装箱。
委托的每个泛型类型参数都可标记为协变量或逆变量。这样就可将泛型委托类型的变量转换为相同的委托类型(但泛型参数类型不同)。泛型类型参数:
不变量:意味着泛型类型参数不能更改。
逆变量:意味着泛型类型参数可以从一个类更改为它的某个派生类。在C#中用in关键字标记逆变量形式的泛型类型参数。逆变量泛型类型参数只出现在输入位置,比如作为方法的参数。
协变量:意味着泛型类型参数可以从一个类更改为它的某个基类。C#是用out关键字标记协变量形式的泛型类型参数。协变量泛型类型参数只能出现在输出位置,比如作为方法的返回类型。
(协变性指定返回类型的兼容性,逆变性指定参数的兼容性)
对于泛型类型参数,如果要将该类型的实参传给使用out或ref关键的方法,便不允许可变性。
定义泛型类,结构或方法时,类型中定义的任何地方都可引用类型指定的类型参数。类型参数可作为方法参数,方法返回值或方法内部定义的局部变量的类型使用。CLR也允许方法指定它自己的类型参数。这些参数也可以作为参数,返回值,或局部变量使用。
泛型方法和类型推断
C#编译器支持在调用泛型方法时进行类型推断。这意味着编译器会在调用泛型方法时自动推断要使用的类型。
在C#中,属性,索引器,事件,操作符方法,构造器和终结器本身不能有类型参数。但它们能在泛型类型中定义,而且这些成员中的代码能使用类型的类型参数。
约束机制:约束的作用是限制能指定成泛型实参的类型数量。通过限制类型的数量,可以对那些类型执行更多操作。
约束可应用于泛型类型的类型参数,也可用于泛型方法的类型参数。CLR不允许基于类型参数名称或约束来进行重载;只能基于元数(类型参数个数)对类型或方法进行重载。
重写虚泛型方法时,重写的方法必须指定相同数量的类型参数,而且这些类型参数继承在基类方法上指定的约束。事实上,根本不允许为重写方法的类型参数指定任何约束。但类型参数的名称是可以改变的。类似地,实现接口方法时,方法必须指定与接口方法等量地类型参数,这些类型参数将继承由接口方法指定地约束。
12.8.1 主要约束
类型参数可以指定零个或者一个主要约束。主要约束可以代表非密封类地一个引用类型。
指定引用类型约束时,相当于向编译器承诺:一个指定的类型实参要么时与约束类型相同的类型,要么时从约束类型派生的类型。
12.8.2 次要约束
类型参数可以指定零个或者多个次要约束,次要约束代表接口类型。这种约束向编译器承诺类型实参实现了接口。由于能指定多个接口约束,所以类型实参必须实现了所有接口约束。
还有一种次要约束称为类型参数约束,有时也称为裸类型约束。这种约束用得比接口约束少得多。它允许一个泛型类型或方法规定:指定的类型实参要么就是约束的类型,要么时约束的类型的派生类。一个类型参数可以指定零个或者多个类型参数约束。
12.8.3 构造器约束
类型参数可指定零个或一个构造器约束,它向编译器承诺类型实参是是实现了公共无参构造器的非抽象类型。where T : new()
12.8.4 其他可验证性问题
1.泛型类型变量的转型
将泛型类型的变量转型为其他类型是非法的,除非转型为与约束兼容的类型。
2.将泛型类型变量设为默认值
将泛型类型变量设为null是非法的,除非将泛型类型约束成引用类型。
3.将泛型类型变量与null进行比较
无论泛型类型是否被约束,使用==或!=操作符将泛型类型变量与null进行比较都是合法的。
4.两个泛型类型变量相互比较
如果泛型类型参数不能肯定是引用类型,对同一个泛型类型的两个变量进行比较是非法的。
5.泛型类型变量作为操作数使用
将操作符应用于泛型类型操作数会出现大量问题。不能将操作符应用于泛型类型的变量。
标签:不同 兼容 替换 优化 派生类 调试 2.3 指针 指令
原文地址:https://www.cnblogs.com/terry-1/p/10028569.html