ToString() 會發生裝箱嗎?

最近被問到了這個問題,我當時回答是會的,因爲ToString()會把值類型轉換成引用類型,所以會發生裝箱。


後來總覺有些不妥當,所以查閱一些資料並參考網絡上的討論:


拆箱裝箱的官方解釋

Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the CLR boxes a value type, it wraps the value inside a System.Object and stores it on the managed heap. Unboxing extracts the value type from the object. Boxing is implicit; unboxing is explicit. The concept of boxing and unboxing underlies the C# unified view of the type system in which a value of any type can be treated as an object.


裝箱用於在託管內存中存儲值類型。 裝箱是是值類型到 object 類型或到此值類型所實現的任何接口類型的隱式轉換。 對值類型裝箱會在堆中分配一個對象實例,並將該值複製到新的對象中。


下面是官方的幾個拆箱/裝箱的例子:

eg1:
int i = 123;
object o = i;//隱式裝箱

eg2:
String.Concat("Answer", 42, true) //42和true都會發生裝箱

eg3:
List<object> mixedList = new List<object>();
mixedList.Add("First Group:");
for (int j = 1; j < 5; j++)
{
    mixedList.Add(j);//在添加時,j先裝箱
}
var sum = 0;for (var j = 1; j < 5; j++)
{
    //下面的一行會發生編譯錯誤: 
    //Operator '*' cannot be applied to operands of type 'object' and 'object'. 
  //sum += mixedList[j] * mixedList[j]);
  
  //下面經過拆箱就不會出現上面的編譯錯誤.
  sum += (int)mixedList[j] * (int)mixedList[j];
}

Note: 

相對於簡單的賦值而言,裝箱和取消裝箱過程需要進行大量的計算。 對值類型進行裝箱時,必須分配並構造一個新對象。 取消裝箱所需的強制轉換也需要進行大量的計算,只是程度較輕。

更多性能的瞭解:https://msdn.microsoft.com/zh-cn/library/ms173196.aspx


再引入圖片來在說明內存中的變化:

int i = 123;
object o = i;//隱式裝箱

wKioL1dk8QeAuJr0AAAIh9hVWI8664.png


int i = 123;      // a value type
object o = i;     // boxing
int j = (int)o;   // unboxing

wKioL1dk8QeSbf--AAAJ2r47a3g046.png


看值類型有沒有進行拆箱,就看他有沒有裝換成Object或者值類型所繼承的接口類型...

Int.ToString 此方法中,值類型轉換成ValueType類型,不滿足裝箱的條件(可以查看下面IL代碼),可以判定Int.ToString是沒有裝箱的。


.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代碼大小       45 (0x2d)
  .maxstack  3
  .locals init ([0] int32 v, [1] object o)
  IL_0000:  nop
  IL_0001:  ldc.i4.5
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  box        [mscorlib]System.Int32
  IL_0009:  stloc.1
  IL_000a:  ldloca.s   v
  IL_000c:  call       instance string [mscorlib]System.Int32::ToString()
  IL_0011:  ldstr      ","
  IL_0016:  ldloc.1
  IL_0017:  unbox.any  [mscorlib]System.Int32
  IL_001c:  box        [mscorlib]System.Int32
  IL_0021:  call       string [mscorlib]System.String::Concat(object,
                                                              object,
                                                              object)
  IL_0026:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002b:  nop
  IL_002c:  ret
} // end of method Program::Main


瞭解了上面的信息之後以後就知道下面建議用哪一個了吧:

int num = 3;
//用下面的哪個呢?請思考
string numStr = string.Format("{0}", num);
string numStr = string.Format("{0}", num.ToString());


參考:

https://msdn.microsoft.com/en-us/library/yz2be5wk.aspx

http://www.cnblogs.com/DebugLZQ/archive/2012/09/02/2667835.html

http://q.cnblogs.com/q/44027

http://bbs.csdn.net/topics/360191652

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