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

  • 相关阅读:
    java开发:分享一下百度ueditor和七牛的图片集成上传
    java开发:分享一下使用urlrewrite实现网址的个性访问
    javascript应用:页面解析list和map封装后的json数据
    JAVA开发:分享一些SpringMvc+Ibatis+spring的框架使用心得
    使用OLEDB读取excel和csv文件
    静态代码块、构造代码块
    序列化和反序列化
    windows下Mysql5.7.10免安装版配置
    form表单中的encType属性
    关于字符编码,你所需要知道的(ASCII,Unicode,Utf-8,GB2312…)
  • 原文地址:https://www.cnblogs.com/gyxdbk/p/2607960.html
Copyright © 2020-2023  润新知