C#中的值类型及装箱

来源:互联网 发布:博思智慧平台c语言答案 编辑:程序博客网 时间:2024/04/30 02:40

我翻译这篇文章写得很好,解释得很清楚,尤其是其图文,让人思路清晰,比MS提供的SDK上请的好懂多了,反正我当年看了好几次还似懂非懂的。我还会翻译我关于C#中的堆与栈的一个系列文章(共四篇),敬请期待哦。

The C# Value Type and Boxing

 



尽管在.NET框架里,我们不需要为内存管理以及垃圾收集操心,但我们还是应该了解它们,来优化我们的应用程序。其中之一便是,公共语言运行环境(CLR)是如何处理对值类型的引用的。

当一个值类型实例被转换成System.Object类型或是接口时,CLR需要把值类型转换为一个恰当的引用类型。然后在托管堆上分配内存并将对象拷上去。我们之所以要了解它,有两个原因:装箱是一个非常耗费资源的过程(将整个对象从栈上拷到堆上会耗费处理器周期以及托管堆空间),我们因此(装箱)而有了两个在内存中可以有相矛盾状态的对象。

这是一个简单的装箱的例子。

int i = 0;

object obj = (object) i;     //装箱
obj = ((int) i) + 3;     // 在堆上改变i的值
i += 1;     // 在栈上改变i的值 

Console.WriteLine(obj.ToString());
Console.WriteLine(i.ToString());

结果:

 

下面来一步一步分析这一过程

第一步:i 被放放置在栈上

 

第二步:i 被拷到堆上(装箱)并将 obj 放到栈上,obj 的值是在堆上的新对象的内在地址(即指针)

 

第三步:在堆上的对象被更新,值变为3

第四步:在栈上的对象 i 被更新,值变为1

下面是一个更复杂的例子:

class Class1

{

    [STAThread]

    static void Main(string[] args)

    {

        MyInt test = new MyInt();

        test.value = 100;

        test.AddOne();

        aDelegate del = new aDelegate(test.AddOne);

        del();

        test.AddOne();

    }

}

public delegate void aDelegate();

public struct MyInt

{

    public int value;

    public void AddOne()

    {

        Console.WriteLine(string.Format("Before: {0}", value.ToString()));

        value += 1;

        Console.WriteLine(string.Format("After : {0}", value.ToString()));

    }

}

下面是输出

结果有没有让你感意外?发生了什么?让我们来分析一下它的过程。在Main()和args[]被置于栈上后,主函数开始执行:

第一步:test被置于栈上

第二步:AddOne()被置于栈上并执行,将Mint.value的值变为101;

第三步:AddOne()被从栈上移除,Myint被装箱(拷)到堆上。现在Del指向MyInt对象在堆上的版本。


第四步:装箱的对象的AddOne()被调用,将其值更新为102.

第五步:来自堆的AddOne()被移除

第六步:Myint对象在栈上的版本(即test)的AddOne()入栈,将其值变为102.

最后:主函数执行完毕,栈被清空。而留在堆上的对象则等待被垃圾回收。

下面让我们做一个改变,来提高我们的应用程序的性能,并使其结果更符合我们的意愿。如果我们将MyInt改为引用类型,我们就只需处理在堆上的一个对象并且没有装箱的过程。

public class MyInt

{

    public int value;

    public void AddOne()

    {

        Console.WriteLine(string.Format("Before: {0}", value.ToString()));

        value += 1;

        Console.WriteLine(string.Format("After : {0}", value.ToString()));

    }

}

修改后,我们得到了我们期望的结果:

Before: 100
After : 101
Before: 101
After : 102
Before: 102
After : 103

总结

当我们在一个值类型上调用ToString()或是GetType(),会有更多的意想不到的装箱过程。因为在这种情况下,方法是从基类(System.Object)被调用,所以必需经过装箱过程后,方法才能被调用。当我们将值类型加入ArrayList()(在这种情况下它们被类型转换为System.Object)时也会发生装箱过程。你可以想象,当装箱过程大规模发生时,将会严重影响程序的性能。

希望这篇文章能让你对值类型的装箱过程,以及在通过引用如接口、委托或Arralist来处理值类型时应注意些什么有更好的理解。

 

 

posted on 2006-08-18 12:50 Nihgwu 阅读(257) 评论(8)  编辑 收藏 引用 收藏至365Key 所属分类: C# 、Translation

评论:
# re: C#中的值类型及装箱[翻译] 2006-08-18 19:48 | 001181
to: obj = ((int) i) + 3; // 在堆上改变i的值

是不是应该这样? obj = ((int) obj) + 3; // 在堆上改变i的值  回复
  
# re: C#中的值类型及装箱[翻译] 2006-08-18 23:27 | Nihgwu
这个其实无所谓  回复
  
# re: C#中的值类型及装箱[翻译] 2006-08-19 08:09 | 001181
怎么说?  回复
  
# re: C#中的值类型及装箱[翻译] 2006-08-21 13:35 | Nihgwu
这个只是让你知道装箱会在堆上从栈复制一个对象,对它的修改不会影响栈上的对象  回复
  
# re: C#中的值类型及装箱[翻译] 2006-08-23 10:08 | OOP
@Nihgwu

int i = 0;
object obj = (object) i; //装箱
obj = ((int) i) + 3; // 在堆上改变i的值
i += 1; // 在栈上改变i的值


可能这段代码你是直接从其他人那里拷贝过来的,代码这样写是没有问题的了,但是下面的翻译和截图是有问题的。
说明如下:
object obj = (object) i; //这行的说明没有问题
obj = ((int) i) + 3; // 这行的说明有问题,正确的应该是
// 将 i + 3 以后装箱,拷贝到堆上, 上一行在堆上建立的对象等待被回收


下面是测试代码
int i = 0;
object obj = (object) i; //装箱,拷贝到堆上
object obj_b = obj; // obj_b也指向堆上的对象
obj = ((int) i) + 3; // 将i+3,在堆上新建一个拷贝
i += 1; // 在栈上改变i的值

Console.WriteLine(obj.ToString());
Console.WriteLine(obj_b.ToString());
Console.WriteLine(i.ToString());

/*
结果

3
0
1
*/


文章写的详细是好的,但是有些细节,对于这样的文章,还是要注意了才好。  回复
  
# re: C#中的值类型及装箱[翻译] 2006-08-23 11:39 | Nihgwu
@OOP
这个问题,我确实想了好久,没注意到你所说的问题,谢了,你说得很对,以后我一定注意  回复
  
# re: C#中的值类型及装箱[翻译] 2006-08-23 23:18 | renzhewudi
@Nihgwu

obj = ((int) i) + 3; // 在堆上改变i的值


对于该句我的疑问如下:
1. i本来就是int型有必要再强制转型吗?
2. 对于((int) i) + 3是基本的值类型运算
3. obj = ((int) i) + 3; 本就是一种装箱,说成是在堆上改变i的值是不合理的

  回复
  
# re: C#中的值类型及装箱[翻译] 2006-08-24 07:30 | Nihgwu
@renzhewudi
在这一点上,OOP说得很对,你可以看看他的解释
我没有改过来就因为原文如此,所以就懒得改了,等我什么时候把C#中的堆与栈的一个系列文章翻译出来,相信会对这一过程有更好的理解,只是这一段时间一直没有时间,就开了个头,其实翻译也是个很麻烦的事,就像这篇一样,在想让别人明白之前首先要自己先有一个完全的理解。当时只是觉得这一点太简单,就没有太在意就直接Copy过来了。
在这给自己打个小广告,昨天我花了几个小时把《C#睡前故事》根据我的理解重新翻译了一遍,难免也会有出错的地方,请指教了  回复
 
Matthew Cochran
原创粉丝点击