众所周知的.net的两种类型是值类型和引用类型,像C#中的int,enum和struct等,还有.net框架中System.Drawing下的Rectangle和point等类型也都是值类型的。很多解决面向对象不久的朋友似乎都会渐渐遗忘了struct等值类型,而是使用不合时宜的class来替代了。值类型有很多好处,比如它们的生存周期较短,并且很少会在多个客户之间共享,正因为这样,合适的使用值类型既提高了性能,又能缓解GC对内存的压力。

以C#为例,值类型的方式可以有下面两种方式:

1int length = 9;
2
3int length = new int(9);

Visual C#的编译器会在stack上分配4bytes的空间来存储值,这样就可以被直接访问到,而不需要再重定向到managed heap上去了。使用new关键字只不过是C#出于一致性的考虑而作的,但是C#的一个特性就是对象的内存空间分配策略是用类型来定义的。

看一个简单的程序:

 1void Main
 2
 3{int a=9;
 4
 5Show();
 6
 7}

 8
 9void Show()
10
11{int a=7;
12
13Console.Write(a);
14
15}

在这个过程中stack中的情况会是:

1. int 9

2. int 9, Show方法的返回地址,int 7

3.int 9

很清晰的可以看出,当Show()方法调用完毕后里面的值就被销毁了。

但是,C#的参数默认是通过值传递的,如果上述的Show()方法变为Show(a)的话,那么stack中将有两个int 9了,作为参数传递进来的是Main()中变量a的copy,而且在Show()中只是用这个copy。随之而来的问题是,如果这时候a是一个复杂的struct,而且其中包含着大量的数据,那作为参数传递时候就会遇到的一个问题了:大型值类型作为参数传递就会牺牲一些性能了,尤其是struct,复制其中的每个值得开销会大大增加,特别是在方法被频繁调用的时候。

如何避免这个问题呢?

先来考虑另一个我们在用值类型作为参数的时候的情景。当我们传递的参数希望在被处理后返回其状态的话,我们需要使用ref关键字,ref关键字大家是如此的熟悉以至于遇到这样的场景我们立刻就会不由自主的用上。为什么可以改变了原来的值呢?因为ref传递的是引用,既然是传递引用,那么就不会创建再复制出一个副本了,因此在使用大型值类型(尤其是struct)的时候,为了避免性能损耗,使用ref修饰会得到性能的提升,但是由此会带来的负面影响是要多多注意了--因为可以改变这个值了,而这是我们在这个场景中不期望的。

interface的功能如此强大,如此的富有意义,值类型当然不会拒绝它。但是interface引用的对象肯定都是分配在heap上的,这个heap是在GC控制之下的,但是值类型肯定是分配在stack上的,这时候使用interface后会是什么样呢?肯定是通过boxing了。看下面的一段伪代码:

interface IA

{int V{g;s;}

}


struct A: IA

{

private int v;

public int V{g{..;}s{..;}}

}




 Main()

{

A a 
= new A();

a.V 
= 9;

IA ia 
= a;

Console.Write(ia.V);

..

}

结果肯定是输出了9。其中一个好玩的地方就是在Write()方法中a将会被boxing,ia引用了一个装箱对象,该对象包含了a的副本。

posted on 2007-02-02 02:41  维生素C.NET  阅读(4133)  评论(22编辑  收藏  举报