技术开发 频道

从微软CLR视角看.net用户自定义类型


构造器


    就像在实例化部分讨论的,值类型既不能也没有缺省构造器。他们可以有参量构造器,通过使用新的输入来明确调用他们。值类型引用类型里的构造器仅仅只是类型的另一个实例方法。编译者暗中使用他们来示例化类型群组,并且执行一些示例化操作。但是当程序被示例化后,编译者不允许程序明确调用类型变量上的构造器。但是在技术上,当一个类型的构造器被示例化后,可以调用一个类型的构造器(例如:在类型实例的构造器的一生中可以调用多次)。通过欺骗编译者,写某些IL代码,就可以完成这一过程。通过使用ILDASM,一个人可以把装配分解为IL,修改IL,包括调用类型构造器,再使用ILASM重新分解配置。然而,并不要求这样做,如果在类型示例的一生中要求多次示例化同一类型实例,开发者可以在类型上定义一个公共的方法,这个类型可以和它的构造器座做同样的工作。

    值类型构造器和参数类型构造器之间的区别是:引用类型构造器应该总是调用他的基类的缺省构造器,这是因为CLR将不会自动执行这一任务,而且完成这一任务是引用类型的责任。但是不要求值类型这样做,原因很明显,因为值类型没有任何缺省构造器,它不能当作基类工作。对于引用类型,在给MSIL编译资源时,编译者(例如:C#)自动把对派生类型构造器的调用插入基类。

继承(区分值类型和引用类型的方法)

    值类型是由一个特别的被称为系统值类型派生而来,而系统值类型是从系统.对象派生而来的。任何一个从系统值类型派生而来的类型被CLR当作值类型对待。CLR使用这一知识工作在类型实例上,正如上面部分以及下面部分描述的。引用类型在他们的基类层次里没有系统值类型。事实上,一些编译器不会允许任何类型直接从系统值类型派生。编译者总是提供一个间接的可执行的方法指定一个类型,此类型是从系统值类型派生而来的,而且在这些类型上执行特定的定义规则。在这些可执行的语言规则的基础上,编译者将产生MSIL代码,此代码适合于值类型。而且必须要求这样,因为当把 代码编成MSIL级(或者处于ILASM中)时,任何一个类型都可以从系统值类型派生出来,而且它可以包括缺省构造器和间接的MSIL指令,值类型中没有这些间接指令和缺省构造器(因为缺省构造器永远都不会被调用)

    值类型不能为其它类型作为基本类型来使用。很明确,一个值类型不能作为引用类型的基本级别,因为值类型没有缺省构造者,它的储存配置与引用类型的储存配置不同。但是为什么值类型不能作为另一个值类型的基本类型?答案在值类型实例的储存配置里。.NET使用方法表来完成执行时多型(虚拟方法分派)。因为一个值类型的实例不能包含方法表,CLR不能正确地使用它来分派虚拟方法调用(下章将讨论方法分派内部原理)。正因为如此,.NET不能为值类型s提供执行时多型。没有执行时多型,从对象为导向的设计角度来看,继承就是不完整的。因此所有的编译者包括ILASM将会把从系统值类型派生的任何类型标记为密封的。任何一个被密封的类型都不能当作基使用,而且在下载一个类型的执行时中,这种限制由CLR来执行。如果CLR发现正在被下载的类型有一个被标志为密封的基本类型,CLR就会阻止下载派生类型,并且引发系统.类型下载异常。

等式和哈希代码

    系统值类型覆盖它的基本类型系统.对象里的相等功能 和Get哈希代码虚拟方法。任何一个从系统值类型派生出来的类型使用相等功能来对比它的实例,而且需要覆盖这两个方法来提高性能。这是因为相等功能方法在比较两个值类型实例时,如果不能在比特级上比较值类型的群组,它就使用反射。在公平比较两个值类型 实例时,消除反射以及与它相关的性能处罚对结构(从系统值类型派生出来的类型)覆盖这些方法以及执行特定的公平检查和哈希代码生成都更好些。

    对于引用类型实例,他们直接或间接的从系统.对象派生而来的,相等功能 and GetHashCode方法安置在系统.对象,而且在目标引用值的基础上执行工作,而不是在类型实例的真实群组values上。

    这两个方法往往结合在一起,因此值类型的两个实例无论是使用相等功能还是使用GetHashCode,都可以同等比较。考虑给用户定义值类型(结构)覆盖字符串. 相等功能以及GetHashCode方法的另一个原因就是:如果使用值类型变量调用这些方法时,却没有将它们覆盖起来,CLR就需要把密封值类型,然后调用值类型上被密封的实例。

实例拷贝语义学(数值和变量分配的方法变元传递)

    值类型实例总是由数值拷贝成同一值类型的另一个变量。当一个方法参量期待同一个值类型 ,且传递值类型的一个变量时作为参数时,这个拷贝就产生了。当指派(在示例化过程中没有示例)一个值类型变量到同一个值类型的另一个变量时,也会发生这个拷贝。值类型可能包括群组,这些群组都是原始的数据类型(不能再被拆开)值类型或者引用类型。如果这个群组是原始类型或者值类型 ,它的数值就会拷贝成和它本身的一样。如果这个群组是引用类型,群组中的值类型就会烤成与目标值类型实例相对应的群组。
0
相关文章