2009-06-22 92 views
10

有些情況下, 值類型的實例需要被視爲引用類型的 實例。對於 這樣的情況下,值 類型實例可通過 過程稱爲裝箱被轉換成一個參考 類型實例。當值 類型實例進行裝箱,倉儲是 在堆上分配和 實例的值複製到該 空間。對該存儲的引用是放置在堆棧上的 。盒裝值 是一個對象, 包含值 類型實例的內容的引用類型。用例在C#中裝箱值類型?

Understanding .NET's Common Type System

Wikipedia有針對Java一個例子。但在C#中,有些情況下需要輸入值類型?或者更好/相似的問題是,爲什麼要在堆上(盒裝)而不是堆棧上存儲值類型?

+0

另請參見[boxing-occurrence-in-c-sharp](http://stackoverflow.com/questions/7995606) – nawfal 2013-06-03 08:03:26

回答

13

一般情況下,你通常會希望避免你的拳擊值類型。

但是,這種情況很少發生,這很有用。例如,如果您需要定位1.1框架,則無法訪問泛型集合。在.NET 1.1中使用集合的任何用途都需要將您的值類型視爲System.Object,從而導致裝箱/取消裝箱。

在.NET 2.0+中仍有這種情況發生。任何時候你想利用包括值類型在內的所有類型都可以直接作爲對象的事實,你可能需要使用裝箱/取消裝箱。這可能有時候很方便,因爲它允許您在集合中保存任何類型(通過在泛型集合中使用對象而不是T),但總的來說,最好避免這種情況,因爲您正在失去類型安全性。然而,經常出現拳擊的一種情況是,當您使用反射時 - 反射中的許多調用在使用值類型時將需要裝箱/取消裝箱,因爲該類型未提前知道。

13

幾乎從來沒有一個很好的理由來刻意填充一個值類型。幾乎總是,填充值類型的原因是將其存儲在某些不能識別類型的集合中。例如,舊的ArrayList是對象的集合,它們是引用類型。收集整數的唯一方法是將它們作爲對象裝箱並將它們傳遞給ArrayList。

現在,我們有泛型集合,所以這是不成問題的。

+4

反射仍然強制您使用box/unbox值,並且非常有價值... – 2009-06-22 17:48:22

+7

非常真實。然而,反思是你想限制的「我絕對不能以其他方式做這件事」的情況,所以我認爲我的第一句話很好。 – Randolpho 2009-06-22 17:51:33

2

我認爲在非泛型集合一樣ArrayList發生在C#中拳擊的一個很好的例子。

1

一個例子是當一個方法接受一個對象參數和值的類型必須傳入。

1

下面是拳擊的一些例子/拆箱

ArrayList ints = new ArrayList(); 
myInts.Add(1); // boxing 
myInts.Add(2); // boxing 

int myInt = (int)ints [0]; // unboxing 

Console.Write("Value is {0}", myInt); // boxing 
+1

示例!=使用案例 – 2009-11-12 16:01:06

1

之一情況下,當發生這種情況時例如,如果你有方法期望類型對象的參數,並且你正在傳入一個基本類型,例如int。或者,如果將參數定義爲int類型的'ref'。

+1

-1。通過引用傳遞時,C#中沒有裝箱。請參閱此處的說明:http://msdn.microsoft.com/en-us/library/14akc2c7.aspx – Randolpho 2009-06-22 18:05:08

+0

+1。每天都有新的東西要學習。 – epitka 2009-06-22 18:09:54

9

拳擊通常在.NET中自動發生,當他們必須;通常當你將一個值類型傳遞給需要引用類型的東西時。一個常見的例子是string.Format()。將原始值類型傳遞給此方法時,它們將作爲調用的一部分進行裝箱。所以:

int x = 10; 
string s = string.Format("The value of x is {0}", x); // x is boxed here 

這示出一個簡單的場景,其中一個值類型(x)被自動盒裝要傳遞給需要的對象的方法。一般而言,您希望儘可能避免使用裝箱值類型......但在某些情況下,它非常有用。

有趣的是,當您在.NET中使用泛型時,值類型在用作參數或類型成員時不會被裝箱。這使得泛型比舊的C#代碼(比如ArrayList)更有效,該代碼將{object}視爲類型不可知的。這增加了使用通用集合的另一個理由,如List<T>Dictionary<T,K>,而不是ArrayListHashtable

5

我建議你埃裏克利珀的2的好文章

http://blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx

http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx

這裏是我會100%,

同意使用堆棧當地人報價 類型只是CLR以您的名義執行的優化。 值類型的相關特性是 它們的語義是 被值複製,而不是有時 它們的重新分配可以通過運行時優化 。

在99%的應用程序開發人員不應該關心價值類型堆棧而不是堆中的原因,以及我們在這裏可以獲得的性能增益。伸出心目中非常簡單的規則:

  1. 避免裝箱/拆箱時不 必要,使用泛型集合。發生 大多數問題不是當你 定義自己的類型,但是當你 使用inproperly (由Microsoft或 的同事定義)現有各類
  2. 讓你的值類型 簡單。如果你需要一個包含10-20個字段的struct ,我想你會更好地創建一個類。試想一下,所有 那場將每次 被複制,當你偶爾按值傳遞給它一個 功能...
  3. 我不認爲這是非常有用的 值類型與引用類型裏面 領域。像結構體 字符串和對象字段。
  4. 根據 所需的功能定義您需要的類型,而不是其中應存儲的 。與 類相比,結構具有 有限的功能,因此如果struct不能提供 所需的功能,如 默認構造函數,則定義類。
  5. 如果某件事可以使用其他 類型的數據執行任何 動作,則通常將其定義爲 類。對於使用 的結構操作,只有在可以將另一種類型轉換爲 時,才應該定義不同的類型 。假設你可以將int加到 double,因爲你可以將int轉換爲 double。
  6. 如果某件事情應該是無國籍的,那它就是一個班級。
  7. 當你猶豫時,請使用引用類型。 :-)

任何規則都允許在特殊情況下排除,但不要試圖過度優化。

p.s. 我遇到了一些具有2 - 3年經驗的ASP.NET開發人員,他們不知道堆棧和堆之間的區別。 :-(我不會僱用這樣的人,如果我是面試官,但不是因爲裝箱/拆箱可以在任何ASP.NET網站是一個瓶頸是我見過。

1

代碼

int x = 42; 
Console.Writeline("The value of x is {0}", x); 

實際上箱和unboxes因爲Writeline做內部的int演員。爲了避免這種情況,你可以做

int x = 42; 
Console.Writeline("The value of x is {0}", x.ToString()); 

小心微妙的錯誤的!

您可以通過聲明自己的類型爲struct來聲明自己的值類型。想象一下,你聲明一個struct有很多屬性,然後在ArrayList中放置一些實例。這當然包裝了他們。現在通過[]運算符引用一個,將其轉換爲該類型並設置一個屬性。您只需在副本上設置一個物業。 ArrayList中的一個仍未修改。

因此,值類型必須始終是不可變的,即使所有成員變量readonly只能在構造函數中設置,並且沒有任何可變類型作爲成員。