随笔 - 13, 文章 - 0, 评论 - 3, 引用 - 0
数据加载中……

.net中装箱的几种情况

本篇所写都是本人想当然的理解。如果这种理解便于帮助你理解一些知识的话,我会感到由衷的欣慰。
   .net中虽然没有指针语法,但是在堆中分配对象,将引用放在栈中,十分类似C++中的指针操作,此时引用就可以看成一种特殊的指针。因为指针操作的间接性,会带来一定的性能影响,为了避免这种影响,.net采取了一种折衷的办法,引入了值类型。

   为了在值类型和引用类型之间进行一些合理的转换,于是带来了装箱和拆箱。
   装箱简单来说就是将值类型转换为引用类型。按三步进行:
(1)新分配托管堆内存(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。
(2)将值类型的实例字段拷贝到新分配的内存中。
(3)返回托管堆中新分配对象的地址。

    拆箱就是将引用类型转换为对应的值类型。分如下步骤进行:
(1)检查引用对象实例,确保它是给定值类型的一个装箱值。
(2)获取引用对象中指向值类型部分的指针。
(3)将引用对象中对应的内容拷贝到值类型区域。

从上面步骤可以看出,装箱和拆箱会给程序的性能带来一定的影响,所以我们应尽可能地避免装箱和拆箱。装箱可以隐式进行,拆箱只能显式进行。只有先装箱,才能拆箱。

为了尽可能地避免装箱和拆箱,我们需要了解装箱的几种情况。
我总结了以下几种(可能不太全面):
(1)方法中参数为Object类,但是传递一个值类型。

1void f(object obj)
2{
3   
4}

5
6static void Main(string[] args)
7{
8   f(32);
9}


(2)一个类型中有field申明为Object类,赋予一个值类型。

 1class Container
 2    {
 3        private object m_obj;
 4
 5        public object Obj
 6        {
 7            get return m_obj; }
 8            set { m_obj = value; }
 9        }

10        
11    }

12    
13
14    class Program
15    {
16        static void Main(string[] args)
17        {
18            Container con;
19            //这里会发生装箱
20            con.Obj = 45;
21        }

22    }


(3)调用Object类中没有被值类型覆盖的方法,如GetType()。

 1 
 2  //值类型,这里借用《.net框架程序设计》中的例子,并做了适当修改
 3    struct MyValType
 4    {
 5        RefType refobj;     //引用类型
 6        ValType valobj;     //值类型
 7
 8        public override bool  Equals(object obj)
 9        {
10            //这里如果这样写,this.GetType(),会将this装箱。
11            //因为MyValType没有覆写GetType()方法,会实际使用Object的GetType()方法
12            //如果要使用GetType()方法,必须先构建方法表,于是发生装箱
13            if (this.GetType() != obj.GetType()) 
14                return false;
15             return this.Equals((MyValType)obj);
16        }

17
18        public Boolean Equals(MyValType obj)
19        {
20            if (!Object.Equals(this.refobj,obj.refobj))
21                return false;
22            if (!this.valobj.Equals(obj.valobj))
23                return false;
24            return true;
25        }

26    }


(4)将值类型转换为成一个被该值类型实现的接口类型。

 1interface IChange
 2    {
 3        void Change(System.Int32 x);
 4    }

 5
 6    struct MyValType: IChange
 7    {
 8        private int value;
 9
10        public int Value
11        {
12          get return this.value; }
13          set this.value = value; }
14        }

15
16        public void Change(System.Int32 x)
17        {
18            value = x;
19        }

20    }

21
22    class Program
23    {
24        static void Main(string[] args)
25        {
26            MyValType valType = new MyValType();
27            valType.Value = 10;
28            //此时会发生装箱
29            IChange iChange = valType;
30            //此时修改,是修改堆中的内存,不会修改valType
31            iChange.Change(20);
32
33            //拆箱
34            MyValType valType2 = (MyValType)iChange;
35
36            //输出10,valType.Value在iChange.Change(20)时不会改变
37            System.Console.WriteLine(valType.Value);
38            //valType2.Value为20
39            System.Console.WriteLine(valType2.Value);
40            System.Console.Read();
41        }

42    }

针对以上四种情况,为了减少装箱和拆箱,建议以如下形式进行:
(1)方法中参数为Object类,但是传递一个值类型。               建议利用方法重载或者泛型。
(2)一个类型中有field申明为Object类,赋予一个值类型。   建议利用泛型。

(3)调用Object类中没有被值类型覆盖的方法,如GetType()。
根据实际情况,判断是否有其它方法实现,如上面举的例子就可以这样修改:

 1//值类型
 2    struct MyValType
 3    {
 4        RefType refobj;     //引用类型
 5        ValType valobj;     //值类型
 6
 7        public override bool  Equals(object obj)
 8        {
 9            //这里不用GetType(),可以避免装箱
10        //同时,因为值类型不能有子类,所以这里用is就可以达到类型比较的目的
11            if (!(obj is MyValType))
12                return false;
13         return this.Equals((MyValType)obj);
14        }

15
16        public Boolean Equals(MyValType obj)
17        {
18            if (!Object.Equals(this.refobj,obj.refobj))
19                return false;
20            if (!this.valobj.Equals(obj.valobj))
21                return false;
22            return true;
23        }

24    }


(4)将值类型转换为成一个被该值类型实现的接口类型。如果设计上真要求这么做,那可能只能如此了。我暂时没有想到什么解法,如果你有更好的解法,希望不吝赐教。

posted on 2009-04-27 23:24 五味杂陈 阅读(1595) 评论(1)  编辑 收藏 引用 所属分类: .NET

评论

# re: .net中装箱的几种情况  回复  更多评论   

高!
2009-04-29 16:59 | 亨德列克

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理