2、装箱是将值范例转换为援用范例 ;
拆箱是将援用范例转换为值范例
应用装箱和拆箱功用,可经由过程许可值范例的任何值与Object 范例的值互相转换,将值范例与援用范例链接起来
比方:
int val = 100; object obj = val; Console.WriteLine (“对象的值 = {0}", obj);
这是一个装箱的历程,是将值范例转换为援用范例的历程
int val = 100; object obj = val; int num = (int) obj; Console.WriteLine ("num: {0}", num);
这是一个拆箱的历程,是将值范例转换为援用范例,再由援用范例转换为值范例的历程
注:被装过箱的对象才被拆箱
3、.NET中,数据范例划分为值范例和援用(不等同于C++的指针)范例,与此对应,内存分派被分红了两种体式格局,一为栈,二为堆(注重:是托管堆)
值范例只会在栈中分派。
援用范例分派内存与托管堆。
托管堆对应于垃圾接纳。
4:装箱/拆箱是什么?
装箱:用于在垃圾接纳堆中存储值范例。装箱是值范例到 object 范例或到此值范例所完成的任何接口范例的隐式转换。
拆箱:从 object 范例到值范例或从接口范例到完成该接口的值范例的显式转换。
5:为什么须要装箱?(为什么要将值范例转为援用范例?)
一种最一般的场景是,挪用一个含范例为Object的参数的要领,该Object可支撑恣意为型,以便通用。当你须要将一个值范例(如Int32)传入时,须要装箱。
另一种用法是,一个非泛型的容器,一样是为了保证通用,而将元素范例定义为Object。因而,要将值范例数据到场容器时,须要装箱。
6:装箱/拆箱的内部操纵
装箱
对值范例在堆中分派一个对象实例,并将该值复制到新的对象中。按三步举行。
新分派托管堆内存(大小为值范例实例大小加上一个要领表指针和一个SyncBlockIndex)。
将值范例的实例字段拷贝到新分派的内存中。
返回托管堆中新分派对象的地点。这个地点就是一个指向对象的援用了。
有人如许明白:假如将Int32装箱,返回的地点,指向的就是一个Int32。我以为也不是不能如许明白,但这确切又有题目,一来它不周全,二来指向Int32并没说出它的实质(在托管堆中)。
拆箱
搜检对象实例,确保它是给定值范例的一个装箱值。将该值从实例复制到值范例变量中。
有书上讲,拆箱只是猎取援用对象中指向值范例部份的指针,而内容拷贝则是赋值语句之触发。我以为这并不要紧。最症结的是搜检对象实例的实质,拆箱和装箱的范例必需婚配,这一点上,在IL层上,看不出道理安在,我的猜想,或许是挪用了相似GetType之类的要领来掏出范例举行婚配(因为须要严厉婚配)。
7:装箱/拆箱对实行效力的影响
明显,从道理上能够看出,装箱时,生成的是全新的援用对象,这会有时间消耗,也就是形成效力下降。
那该怎样做呢?
起首,应当只管防止装箱。
比方上例2的两种状况,都能够防止,在第一种状况下,能够经由过程重载函数来防止。第二种状况,则能够经由过程泛型来防止。
固然,凡事并不能相对,假定你想革新的代码为第三方顺序集,你没法变动,那你只能是装箱了。
关于装箱/拆箱代码的优化,因为C#中对装箱和拆箱都是隐式的,所以,基本的要领是对代码举行剖析,而剖析最直接的体式格局是相识道理结何检察反编译的IL代码。
比方:在循环体中能够存在过剩的装箱,你能够简朴采纳提早装箱体式格局举行优化。
8:对装箱/拆箱更进一步的相识
装箱/拆箱并不如上面所讲那末简朴明了
比方:装箱时,变成援用对象,会多出一个要领表指针,这会有何用途呢?
我们能够经由过程示例来进一步讨论。
举个例子:
Struct A : ICloneable { public Int32 x; public override String ToString() { return String.Format(”{0}”,x); } public object Clone() { return MemberwiseClone(); } } static void main() { A a; a.x = 100; Console.WriteLine(a.ToString()); Console.WriteLine(a.GetType()); A a2 = (A)a.Clone(); ICloneable c = a2; Ojbect o = c.Clone(); }
a.ToString()。编译器发明A重写了ToString要领,会直接挪用ToString的指令。因为A是值范例,编译器不会涌现多态行动。因而,直接挪用,不装箱。(注:ToString是A的基类System.ValueType的要领)
a.GetType(),GetType是继续于System.ValueType的要领,要挪用它,须要一个要领表指针,因而a将被装箱,从而生成要领表指针,挪用基类的System.ValueType。(补一句,一切的值范例都是继续于System.ValueType的)。
a.Clone(),因为A完成了Clone要领,所以无需装箱。
ICloneable转型:当a2为转为接口范例时,必需装箱,因为接口是一种援用范例。
c.Clone()。无需装箱,在托管堆中对上一步已装箱的对象举行挪用。
附:实在上面的基于一个基本的道理,因为未装箱的值范例没有要领表指针,所以,不能经由过程值范例来挪用其上继续的虚要领。别的,接口范例是一个援用范例。对此,我的明白,该要领表指针相似C++的虚函数表指针,它是用来完成援用对象的多态机制的重要依据。
9:怎样变动已装箱的对象
关于已装箱的对象,因为没法直接挪用其指定要领,所以必需先拆箱,再挪用要领,但再次拆箱,会生成新的栈实例,而没法修正装箱对象。有点晕吧,觉得在说绕口令。照样举个例子来讲:(在上例中追加change要领)
public void Change(Int32 x) { this.x = x; }
挪用:
A a = new A(); a.x = 100; Object o = a; //装箱成o,下面,想转变o的值 ((A)o).Change(200); //改掉了吗?没改掉没改掉的缘由是o在拆箱时,生成的是暂时的栈实例A,所以,修改是基于暂时A的,并未改到装箱对象。
(附:在托管C++中,许可直接取加拆箱时第一步获得的实例援用,而直接变动,但C#不可。)
那该如之奈何?
嗯,经由过程接口体式格局,能够到达雷同的结果。
完成以下:
interface IChange { void Change(Int32 x); } struct A : IChange { … }
挪用:
((IChange)o).Change(200);//改掉了吗?改掉了
为啥如今能够改?
在将o转型为IChange时,这里不会举行再次装箱,固然更不会拆箱,因为o已经是援用范例,再因为它是IChange范例,所以能够直接挪用Change,因而,变动的也就是已装箱对象中的字段了,到达希冀的结果。
10、将值范例转换为援用范例,须要举行装箱操纵(boxing):
起首从托管堆中为新生成的援用对象分派内存
然后将值范例的数据拷贝到方才分派的内存中
返回托管堆中新分派对象的地点
能够看出,举行一次装箱要举行分派内存和拷贝数据这两项比较影响机能的操纵。
将援用范例转换为值范例,须要举行拆箱操纵(unboxing):
起首猎取托管堆中属于值范例那部份字段的地点,这一步是严厉意义上的拆箱。
将援用对象中的值拷贝到位于线程客栈上的值范例实例中。
经由这2步,能够以为是同boxing是互反操纵。严厉意义上的拆箱,并不影响机能,但陪伴这以后的拷贝数据的操纵就会同boxing操纵中一样影响机能。
11、
NET的一切范例都是由基类System.Object继续过来的,包含最经常使用的基本范例:int, byte, short,bool等等,就是说一切的事物都是对象。
假如说明这些范例得时刻都在堆(HEAP)中分派内存,会形成极低的效力!(其中缘由以及关于堆和栈得区分会在另一篇里单独得说说!)
.NET怎样处理这个题目得了?恰是经由过程将范例分红值型(value)和援用型(regerencetype),
C#中定义的值范例和援用范例
值范例:原范例(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、罗列(enum)、构造(struct)
援用范例:类、数组、接口、托付、字符串等
值型就是在栈中分派内存,在说明的同时就初始化,以确保数据不为NULL;
援用型是在堆中分派内存,初始化为null,援用型是须要GARBAGE COLLECTION来接纳内存的,值型不必,超出了作用局限,体系就会自动开释!
下面就来讲装箱和拆箱的定义!
装箱就是隐式的将一个值型转换为援用型对象。比方:
int i=0; Syste.Object obj=i;
这个历程就是装箱!就是将i装箱!
拆箱就是将一个援用型对象转换成恣意值型!比方:
int i=0; System.Object obj=i; int j=(int)obj;
这个历程前2句是将i装箱,后一句是将obj拆箱!
更多c#装箱和拆箱学问整顿相干文章请关注ki4网!