有趣的是,不管是引用类型还是值类型,c#语言允许你使用同样的new操作符。当在引用类型上使用时.c#的new操作符被转换成一条CILnewobje指令。它在调用类型的构造函数之后,将引发在堆上的分配行为。当你使用值类型时,CLR把C#的new操作符转换成一条CIL initobi指令。它只是初始化实例,给每个字段赋予默认值。由此可见,对值类型使用new操作符,与在c++中通过operator new调用构造函数而不分配内存足一样的。
值类型和引用类型的赋值操作是不同的。对于引用类型,赋值只是简单地复制对原实例的一个引用,这导致在内存中两个变量将引用同一个实例。对于值类型,赋值是将一个实例的内容改写为其他实例的内容。于是,在赋值完成后,两个实例完全没有关系。比较示例5.2的代码(如图5.4所示)和示例5.3的代码(如图5.5所示)。注意引用类型的情形,赋值仅仅是复制引用,并且对一个变量的改变对于另一个变量而言是可见的,相比之下,值类型的赋值产生了另外一个独立的实例。在值类型的例子中,vl和v2代表size类型的两个不同实例。而在引用类型的例于中,rl和r2只是CSize类型的一个实例的两个名字而已。
示例5.2使用引用类型
static App {
static void Main()
{
CSize r1=new CSize();// r1引用CSize的实例
CSize r2=r1; //r2指向与r1相同的对象
R1.heigth=100;
R2.height=200;
Bool truth=r2.height==r1.height;
Bool moreTruth=r1.height==200;
}
}
示例2.3使用值类型
static App {
static void Main()
{
CSize v1=new CSize();// v1引用CSize的实例
CSize v2=v1; //r2指向与v1相同的对象
v1.heigth=100;
v2.height=200;
Bool truth=v2.height!=v1.height;
}
}
特别要注意的是,向方法传递参数是赋值的变体。当向方法传递参数时,方法的声明决定参数是按引用传递还是按值传递。按值传递参数(默认情形)将导致该方法或者被调用方( callee)具有自己的该参数值的私有拷贝。如图5.6所示,如果参数是值类型,方法将得到它自己的该实例的私有拷贝。
如果参数是引用类型,那么,按值传递的是引用(而小是实例)。该引用所指向的对象是不会被拷贝的。更进一步说,调用方( caller)和被调用方(callee)各自引用的是同一个共亨对象。
按引用传递参数(在c#中用ref或out修饰符标识)导致方法或被调用方得到一个托管指针.它指向调用方的变量,如图5.7所示,方法对值类型或引用类型所做的任何修改对于调用方都是可见的,并且,假如这个方法对一个对象引用的参数进行修改,使之
重定向到内存中的另一外个对象,那么,这个改变也将影响调用方的变量。
在图5.7所示的例子中,被调用方的方法体在a或b参数上执行的任何赋值操作,都将影响调用方。
特别是假设将b参数设为null,调用方的y变量也会相应地被改为null。相比之下,在图5.6所示的示例中,被调用方的方法体可以随意地给a或b参数赋值,而不会对调用方有任何影响。不过,b参数引用的对象与调用方是共享的,并且调用方将看到通过b所做的任何改变.在两个示例中都是如此。裕琌bjcct.Equals的默认实现只是简单地测试同一性,这意味着只有当两个对象实际上是同一个对象时该方法才会返回真。