没有合适的资源?快使用搜索试试~ 我知道了~
本页内容编译器如何处理范型?规则和限制whereT:IComparable范型接口和委托类库中的范型小结在本专栏某一期中,我初步讨论了公共语言运行库(CLR)中的范型。我引入了范型的概念,介绍了范型所带来的灵活性和代码重用,探讨了性能和类型安全,并通过一个简单的代码示例展示了C#中的范型语法。本月,我将深入讨论与范型有关的CLR内部工作机制。我将介绍类型约束、范型类、方法、结构和即将问世的范型类库。编译器如何处理范型?C++模板和Java语言中提议的范型等效都是它们各自编译器的功能。这些编译器在编译时根据对范型或模板类型的引用来构造代码。这会导致代码臃肿并降低结构之间的类型等效(即使类型变量相
资源推荐
资源详情
资源评论
CLR中的范型详解中的范型详解
本页内容本页内容
编译器如何处理范型?
规则和限制
where T :
IComparable
范型接口和委托
类库中的范型
小结
在本专栏某一期中,我初步讨论了公共语言运行库 (CLR) 中的范型。我引入了范型的
概念,介绍了范型所带来的灵活性和代码重用,探讨了性能和类型安全,并通过一个
简单的代码示例展示了 C# 中的范型语法。本月,我将深入讨论与范型有关的 CLR 内
部工作机制。我将介绍类型约束、范型类、方法、结构和即将问世的范型类库。
编译器如何处理范型?编译器如何处理范型?
C++ 模板和 Java 语言中提议的范型等效都是它们各自编译器的功能。这些编译器在
编译时根据对范型或模板类型的引用来构造代码。这会导致代码臃肿并降低结构之间
的类型等效(即使类型变量相同)。相反,CLR 范型不采用这种工作方式。
CLR 中的范型是平台本身出类拔萃的功能。要通过这种方式实现它就需要更改整个
CLR(包括新的和修改过的中间语言指令),并更改元数据、类型加载器、实时 (JIT)
编译器、语言编译器等等。这对 CLR 中的运行时扩展有两个重要的好处。
首先,即使范型的每个结构(例如 Node首先,即使范型的每个结构(例如
Node<Form> 和 Node<String>)都有自己独特的类型标识,但 CLR 能够在类型实例
化期间重用许多真正的 JIT 编译的代码。这极大地降低了代码膨胀,并且也是切实可
行的,因为范型的各种实例化都是在运行时才展开的。在编译时,构造类型的所有内
容就是类型引用。当程序集 A 和 B 都引用在第三方程序集中定义的范型时,它们的构
造类型就会在运行时展开。这意味着,除了共享 CLR 类型标识(在适当的时候)以
外,来自程序集 A 和 B 的类型实例化也共享运行时资源,如本机代码和扩展的元数
据。
类型等效是构造类型运行时扩展的第二个好处。以下为一个示例:引用 AssemblyA.dll
中构造 Node <Int32> 的代码和引用 AssemblyB.dll 中构造 Node <Int32> 的代码都会
在运行时创建具有相同 CLR 类型的对象。通过这种方式,如果两个程序集由同一个应
用程序使用,则它们的 Node <T> 类型的结构会解析为相同的类型,并且它们的对象
可以自由交换。应该注意的是,编译时扩展会使得这种逻辑上很简单的等效变得有问
题或者无法实现。
在运行库级别(而非编译器级别)实现范型还有其他一些好处。其中一个好处是范型
信息会在编译和执行期间保留下来,因此在代码生存期的任何时刻都可以访问它。例
如,反射提供对范型元数据的完全访问。另一个好处是 Visual Studio .NET 中丰富的
IntelliSense 支持,以及范型代码所带来的舒心的调试体验。相反,Java 范型和 C++
模板在运行时会失去它们的范型标识。
另一个好处(也是 CLR 的支柱)是交叉语言使用 — 使用一种托管语言定义的范型可
以由用另一种托管语言编写的代码引用。同时,由于许多繁重的工作都是在这个平台
上完成的,所以语言供应商在他们的编译器中置入范型支持的可能性与日剧增。
在运行时类型扩展的众多好处中,我最喜欢的那一个就显得微不足道了。范型代码只
限明确用于类型构造实例化的操作使用。这种限制的附加好处是,使 CLR 范型比与它
们相应的 C++ 模板更好理解,也更加有用。让我们看一下 CLR 中对范型的限制。
规则和限制规则和限制
一个困扰使用 C++ 模板的编程人员的问题是许多对类型结构所作的特殊尝试都会失
败,包括类型参数的类型变量在实现由模板化代码调用的方法时会失败。同时,这些
情况下的编译器错误也很令人困扰,而且可能看起来与根本问题不相关。采用构造类
型的运行时扩展以后,类似的错误会变成 JIT 编译器错误或类型加载错误,而不是编
译时错误。CLR 架构师决定了对于范型来说,这种实现是不可接受的。
相反,他们决定了对于范型(例如 Node <T>)的任何可能的类型实例化,即使类型
实例化实际发生在运行时,该范型也必须在编译时被证实为一种有效类型。同样,有
问题的类型结构周围的扩展错误不可能出现。为了实现这个目标,架构师通过一组规
则和限制来约束范型的功能,从而保证在尝试扩展其中一个范型实例化之前这种范型
有效。
有一些规则限制了您通常可以编写的代码的类型。这些规则的本质可以归纳为一句
话:范型代码只有在用于范型的每个可能的构造实例时才有效。否则,范型代码无
效,并且也不能正确编译(或者可以编译,但无法在运行时通过验证)。
首先,它看起来像是一个限制规则。以下为一个示例:
public class GenericMath {
public T Min<T>(T item1, T item2) {
if (item1 < item2) {
return item1;
}
return item2;
}
}
这段代码在 CLR 范型中是无效的。C# 编译器产生的错误如下所示:
invalid.cs(4,11): error CS0019: Operator '<' cannot be applied to
operands of type 'T' and 'T'
同时,除了细微的语法区别外,与这基本相同的代码在 C++ 模板中是允许的。为什么
对范型有这样的限制呢?原因是:在 C# 中,“<”运算符只能用于特定的类型。然而,
前面代码片段中的类型参数 T 可以在运行时扩展为任何 CLR 类型。前面的代码示例
不是在运行时被认为是无效的,而是在编译时被认为是无效的。
除了运算符,更多可管理的类型使用(例如方法调用)也应用了相同的限制。以下对
Min <T> 方法的修改也是无效的范型代码:
class GenericMath {
public T Min<T>(T item1, T item2) {
if (item1.CompareTo(item2) < 0) {
return item1;
}
return item2;
}
}
这段代码无效的原因与前面的示例是一样的。虽然类库中的许多类型都实现了
CompareTo 方法,而且该方法也很容易由您的自定义类型实现,但不能保证这个方法
适用于可用作 T 的参数的任何可能的类型。
但您也可以看出,范型代码中并非完全禁止方法调用。GetHashCode 方法在两个参
数化变量中调用,Node<T> 类型在它的参数化 m_data 字段中调用了 ToString 方
法。为什么允许 GetHashCode 和 ToString,而不允许 CompareTo 呢?原因在
于,GetHashCode 和 ToString 都是在 System.Object 类型中定义的,而每个可能的
CLR 类型也是从这个类型派生的。这意味着,类型 T 的每个可能的扩展都实现了
ToString 和 GetHashCode 成员函数。
如果要使范型可用于集合类以外的任何类,则范型代码需要能够调用由
System.Object 定义的方法以外的方法。不过要记住,只有当用于范型的任何可能的
构造实例时,范型代码才有效。有一个办法可以解决这两个看似相互矛盾的要求,那
就是 CLR 范型中称为约束的功能。
您应该知道,约束是范型或方法定义的一个可选组件。在可作为变量用于范型代码上
的一个类型参数的类型中,一个范型可以定义任意数量的约束,而每个约束可以应用
任一个限制。通过限制可在范型结构中使用的类型,对引用受限类型参数的代码的限
制就可以放松一些。
因为对类型参数 T 应用了约束,所以 Min <T> 和 Max <T> 在其条目上调用
CompareTo 是有效的。
where T : IComparable
这个约束表明 Min <T> 方法的任何结构都必须为实现 IComparable 接口的类型的参数
T 提供一个类型变量。这个约束限制了 Min <T> 的可能实例化的种类,但提高了方法
中代码的灵活性,比如现在可以在类型 T 的变量上调用 CompareTo。
约束通过允许范型代码调用扩展类型上的任意方法,从而使范型算法成为可能。虽然
约束要求使用额外的语法才能定义范型代码,但约束不会改变引用代码的语法。引用
剩余10页未读,继续阅读
资源评论
weixin_38663113
- 粉丝: 5
- 资源: 896
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功