爲什麼我們需要在C#中裝箱和拆箱?

本文翻譯自:Why do we need boxing and unboxing in C#?

Why do we need boxing and unboxing in C#? 爲什麼我們需要在C#中裝箱和拆箱?

I know what boxing and unboxing is, but I can't comprehend the real use of it. 我知道拳擊和拆箱是什麼,但我無法理解它的實際用途。 Why and where should I use it? 我應該在哪裏以及在哪裏使用它?

short s = 25;

object objshort = s;  //Boxing

short anothershort = (short)objshort;  //Unboxing

#1樓

參考:https://stackoom.com/question/8rOD/爲什麼我們需要在C-中裝箱和拆箱


#2樓

In general, you typically will want to avoid boxing your value types. 通常,您通常希望避免裝箱值類型。

However, there are rare occurances where this is useful. 但是,很少有這種情況有用。 If you need to target the 1.1 framework, for example, you will not have access to the generic collections. 例如,如果您需要定位1.1框架,則無法訪問泛型集合。 Any use of the collections in .NET 1.1 would require treating your value type as a System.Object, which causes boxing/unboxing. 在.NET 1.1中使用集合需要將您的值類型視爲System.Object,這會導致裝箱/取消裝箱。

There are still cases for this to be useful in .NET 2.0+. 仍有一些情況可以在.NET 2.0+中使用。 Any time you want to take advantage of the fact that all types, including value types, can be treated as an object directly, you may need to use boxing/unboxing. 只要您想利用所有類型(包括值類型)可以直接作爲對象這一事實,您可能需要使用裝箱/拆箱。 This can be handy at times, since it allows you to save any type in a collection (by using object instead of T in a generic collection), but in general, it is better to avoid this, as you're losing type safety. 這有時很方便,因爲它允許您在集合中保存任何類型(通過在通用集合中使用對象而不是T),但一般來說,最好避免這種情況,因爲您正在失去類型安全性。 The one case where boxing frequently occurs, though, is when you're using Reflection - many of the calls in reflection will require boxing/unboxing when working with value types, since the type is not known in advance. 但是,經常發生拳擊的一種情況是,當你使用反射時 - 反射中的許多調用在處理值類型時需要裝箱/拆箱,因爲事先不知道類型。


#3樓

Boxing and Unboxing are specifically used to treat value-type objects as reference-type; Boxing和Unboxing專門用於將值類型對象視爲引用類型; moving their actual value to the managed heap and accessing their value by reference. 將其實際值移動到託管堆並通過引用訪問它們的值。

Without boxing and unboxing you could never pass value-types by reference; 沒有裝箱和拆箱,你永遠不能通過引用傳遞值類型; and that means you could not pass value-types as instances of Object. 這意味着您無法將值類型作爲Object的實例傳遞。


#4樓

Why 爲什麼

To have a unified type system and allow value types to have a completely different representation of their underlying data from the way that reference types represent their underlying data (eg, an int is just a bucket of thirty-two bits which is completely different than a reference type). 要有一個統一的類型系統,並允許值類型從引用類型表示其基礎數據的方式中獲得完全不同的基礎數據表示(例如, int只是一個32位的桶,與a完全不同參考類型)。

Think of it like this. 想想這樣。 You have a variable o of type object . 你有一個類型object的變量o And now you have an int and you want to put it into o . 現在你有一個int ,你想把它放進o o is a reference to something somewhere, and the int is emphatically not a reference to something somewhere (after all, it's just a number). o是對某個地方的引用,而int強調的不是對某個地方的引用(畢竟,它只是一個數字)。 So, what you do is this: you make a new object that can store the int and then you assign a reference to that object to o . 所以,你要做的是:創建一個可以存儲int的新object ,然後將該對象的引用分配給o We call this process "boxing." 我們稱這個過程爲“拳擊”。

So, if you don't care about having a unified type system (ie, reference types and value types have very different representations and you don't want a common way to "represent" the two) then you don't need boxing. 所以,如果你不關心擁有統一的類型系統(即,引用類型和值類型具有非常不同的表示,並且你不想要一種“代表”兩者的常用方法),那麼你不需要裝箱。 If you don't care about having int represent their underlying value (ie, instead have int be reference types too and just store a reference to their underlying value) then you don't need boxing. 如果你不關心int表示它們的底層值(即, int也是引用類型而只是存儲對它們的基礎值的引用)那麼你不需要裝箱。

where should I use it. 我應該在哪裏使用它。

For example, the old collection type ArrayList only eats object s. 例如,舊的集合類型ArrayList僅佔用object s。 That is, it only stores references to somethings that live somewhere. 也就是說,它只存儲對某些地方的某些東西的引用。 Without boxing you cannot put an int into such a collection. 沒有拳擊,你不能把int放入這樣的集合。 But with boxing, you can. 但是對於拳擊,你可以。

Now, in the days of generics you don't really need this and can generally go merrily along without thinking about the issue. 現在,在仿製藥的時代,你真的不需要這個,並且通常可以快樂地走,而不考慮問題。 But there are a few caveats to be aware of: 但有一些需要注意的注意事項:

This is correct: 這是對的:

double e = 2.718281828459045;
int ee = (int)e;

This is not: 這不是:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)o; // runtime exception

Instead you must do this: 相反,你必須這樣做:

double e = 2.718281828459045;
object o = e; // box
int ee = (int)(double)o;

First we have to explicitly unbox the double ( (double)o ) and then cast that to an int . 首先,我們必須顯式地取消double(double)o ),然後將其轉換爲int

What is the result of the following: 以下是什麼結果:

double e = 2.718281828459045;
double d = e;
object o1 = d;
object o2 = e;
Console.WriteLine(d == e);
Console.WriteLine(o1 == o2);

Think about it for a second before going on to the next sentence. 在繼續下一句話之前,請考慮一下。

If you said True and False great! 如果你說True False Wait, what? 等等,什麼? That's because == on reference types uses reference-equality which checks if the references are equal, not if the underlying values are equal. 那是因爲引用類型上的==使用引用相等性來檢查引用是否相等,而不是基礎值是否相等。 This is a dangerously easy mistake to make. 這是一個非常容易犯的錯誤。 Perhaps even more subtle 也許更微妙

double e = 2.718281828459045;
object o1 = e;
object o2 = e;
Console.WriteLine(o1 == o2);

will also print False ! 也將打印False

Better to say: 更好地說:

Console.WriteLine(o1.Equals(o2));

which will then, thankfully, print True . 然後,謝天謝地,打印出True

One last subtlety: 最後一個微妙之處:

[struct|class] Point {
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Point p = new Point(1, 1);
object o = p;
p.x = 2;
Console.WriteLine(((Point)o).x);

What is the output? 什麼是輸出? It depends! 這取決於! If Point is a struct then the output is 1 but if Point is a class then the output is 2 ! 如果Point是一個struct那麼輸出是1但如果Point是一個class那麼輸出是2 A boxing conversion makes a copy of the value being boxed explaining the difference in behavior. 裝箱轉換使得裝箱的值的副本解釋了行爲的差異。


#5樓

The last place I had to unbox something was when writing some code that retrieved some data from a database (I wasn't using LINQ to SQL , just plain old ADO.NET ): 我必須解開的最後一個地方是編寫一些從數據庫中檢索一些數據的代碼(我沒有使用LINQ to SQL ,只是簡單的舊ADO.NET ):

int myIntValue = (int)reader["MyIntValue"];

Basically, if you're working with older APIs before generics, you'll encounter boxing. 基本上,如果你在泛型之前使用舊的API,你會遇到拳擊。 Other than that, it isn't that common. 除此之外,它並不常見。


#6樓

Boxing isn't really something that you use - it is something the runtime uses so that you can handle reference and value types in the same way when necessary. 拳擊並不是你真正使用的東西 - 它是運行時使用的東西,因此你可以在必要時以相同的方式處理引用和值類型。 For example, if you used an ArrayList to hold a list of integers, the integers got boxed to fit in the object-type slots in the ArrayList. 例如,如果您使用ArrayList來保存整數列表,則整數將被裝箱以適合ArrayList中的對象類型插槽。

Using generic collections now, this pretty much goes away. 現在使用通用集合,這幾乎消失了。 If you create a List<int> , there is no boxing done - the List<int> can hold the integers directly. 如果創建List<int> ,則沒有完成裝箱 - List<int>可以直接保存整數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章