1. Boxing und Unboxing sind abstrakte Konzepte
2. Boxing wandelt Werttypen in Referenztypen um.
Verwendung der Boxing- und Unboxing-Funktionen. Werttypen können mit Referenztypen verknüpft werden, indem ermöglicht wird, dass jeder Wert des Werttyps in den und vom Objekttyp konvertiert wird.
Zum Beispiel:
Dies ist ein Prozess des Boxens, Dabei handelt es sich um den Prozess der Konvertierung von Werttypen in Referenztypenint val = 100; object obj = val; Console.WriteLine (“对象的值 = {0}", obj);
int val = 100; object obj = val; int num = (int) obj; Console.WriteLine ("num: {0}", num);
Hinweis: Nur geboxte Objekte können entboxt werden
3. In .NET sind Datentypen in Werttypen und Referenztypen (nicht äquivalent zu C++-Zeigern) unterteilt. Dementsprechend ist die Speicherzuweisung in zwei Teile unterteilt Methoden, eine ist der Stapel und die andere ist der Heap (Hinweis: Es handelt sich um einen verwalteten Heap) Werttypen werden nur auf dem Stapel zugewiesen.
Referenztypen weisen Speicher und verwalteten Heap zu.
Der verwaltete Heap entspricht der Garbage Collection.
4: Was ist Boxen/Unboxing?
Unboxing: Eine explizite Konvertierung von einem Objekttyp in einen Werttyp oder von einem Schnittstellentyp in einen Werttyp, der die Schnittstelle implementiert.
5: Warum wird eine Verpackung benötigt? (Warum Werttypen in Referenztypen konvertieren?)
Eine andere Verwendung ist ein nicht generischer Container. Um die Universalität sicherzustellen, wird der Elementtyp als Objekt definiert. Daher ist beim Hinzufügen von Werttypdaten zu einem Container eine Boxung erforderlich.
6: Interne Operationen des Boxing/Unboxing
Boxing
Ordnet eine Objektinstanz im Heap für einen Werttyp zu und kopiert den Wert in ein neues Objekt. Befolgen Sie drei Schritte.
Neu zugewiesener verwalteter Heap-Speicher (die Größe entspricht der Größe der Werttypinstanz plus einem Methodentabellenzeiger und einem SyncBlockIndex).
Gibt die Adresse des neu zugewiesenen Objekts im verwalteten Heap zurück. Diese Adresse ist eine Referenz auf das Objekt.
Manche Leute verstehen es so: Wenn Int32 geboxt ist, verweist die zurückgegebene Adresse auf einen Int32. Ich denke, es ist nicht unmöglich, es auf diese Weise zu verstehen, aber es gibt Probleme. Erstens ist es nicht umfassend, und zweitens sagt der Verweis auf Int32 nicht seinen Kern aus (im verwalteten Heap).
Unboxing
Laut einigen Büchern erhält man beim Unboxing nur den Zeiger auf den Werttypteil des Referenzobjekts, und das Kopieren des Inhalts ist der Auslöser der Zuweisungsanweisung. Ich denke nicht, dass es wichtig ist. Das Wichtigste ist, die Art der Objektinstanz zu überprüfen. An diesem Punkt kann ich auf der IL-Ebene nicht erkennen, dass möglicherweise eine Methode wie GetType aufgerufen wird . Nehmen Sie den Typ für den Abgleich heraus (da ein strikter Abgleich erforderlich ist).
7: Der Einfluss des Boxens/Unboxings auf die Ausführungseffizienz
Offensichtlich ist aus dem Prinzip ersichtlich, dass beim Boxen ein brandneues Referenzobjekt generiert wird, das Zeit in Anspruch nimmt. das heißt, was zu einer verringerten Effizienz führt.
Was tun?Zuallererst sollte Boxen so weit wie möglich vermieden werden.
Zum Beispiel können beide Situationen in Beispiel 2 oben vermieden werden. Im ersten Fall kann dies durch Überlastung der Funktion vermieden werden. Der zweite Fall kann durch Generika vermieden werden.
Natürlich ist nicht alles absolut. Angenommen, der Code, den Sie ändern möchten, ist eine Assembly eines Drittanbieters und Sie können ihn nicht ändern, dann können Sie ihn nur einpacken.
Für die Optimierung des Boxing-/Unboxing-Codes besteht die grundlegende Methode darin, den Code zu analysieren, da Boxing und Unboxing in C# implizit sind. Der direkteste Weg zur Analyse besteht darin, die Grundstruktur zu verstehen. Sehen Sie sich den dekompilierten IL-Code an.
Zum Beispiel: Im Schleifenkörper kann es zu redundanten Boxen kommen, und Sie können zur Optimierung einfach die Vorabboxung verwenden.
8: Besseres Verständnis des Einpackens/Auspackens
Das Einpacken/Auspacken ist nicht so einfach und klar wie oben erwähnt
Zum Beispiel: Ändern Sie beim Einpacken, um auf ein Objekt zu verweisen, Es wird einen zusätzlichen Methodentabellenzeiger geben. Wozu dient das?
Wir können anhand von Beispielen weiter erforschen.
Zum Beispiel:
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); //改掉了吗?没改掉
(附:在托管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#装箱和拆箱知识整理相关文章请关注PHP中文网!