Effective C#:儘量減少裝箱和拆箱
裝箱和拆箱存在的意義:值型別是資料的容器,它儲存在堆疊上,不具備多型性,而.NET框架在整個物件層次的設計中,使用System.Object作為所有型別的基類,但是Obejct是引用型別,而作為值型別的基類System.ValueType,是從System.Object派生出來的,這就產生了矛盾,裝箱和拆箱就是為了解決這兩種型別之間的差異。
裝箱會將一個值型別放入一個未具名型別(untyped)的引用物件中,從而允許該值型別應用於那些只能使用引用型別的場合。拆箱則會從前面的裝箱物件中提取出一個值型別的副本。裝箱和拆箱都是比較耗時的操作。
裝箱操作會將值型別轉換為一個引用型別,這個過程中會建立一個新的引用獨享,然後將其分配到堆上,同時值型別的副本會被儲存在該引用物件內部。當我們需要從裝箱物件中獲取任何資訊時,會建立值型別的一個副本,然後將其返回。其中的關鍵是:當我們需要引用型別時,會建立一個新的引用型別的物件並將其放入到堆中;當我們需要訪問已經裝箱的物件資訊時,就會建立對應值型別的一個副本,並將其返回。
裝箱和拆箱最大的問題是它們會自動發生。當我們使用的是值型別,而期望的是引用型別,那麼編譯器就會自動產生裝箱和拆箱語句。
我們來看下面的語句,居然也發生了裝箱和拆箱操作。
Console.WriteLine("A few numbers:{0}, {1}, {2}",
25, 32, 50);
上述程式碼之所以發生了裝箱,是因為WriteLine方法需要的引數型別是System.Object,而25是一個int型別,屬於值型別,因此需要裝箱,而在WriteLine方法內部實現時,需要呼叫方法引數的ToString()方法,為了呼叫裝箱物件的方法,就會發生拆箱的操作。
為了避免裝箱和拆箱,可以將上述程式碼進行如下修改。
Console.WriteLine("A few numbers:{0}, {1}, {2}",
25.ToString(), 32.ToString(), 50.ToString());
另外,由於裝箱和拆箱都會產生新的例項,那麼有時會產生一些詭異的bug,我們來檢視下面的程式碼。
程式碼
1 public struct Person
2 {
3 private string _Name;
4
5 public string Name
6 {
7 get
8 {
9 return _Name;
10 }
11 set
12 {
13 _Name = value;
14 }
15 }
16
17 public override string ToString( )
18 {
19 Return _Name;
20 }
21 }
22
23 // Using the Person in a collection:
24 ArrayList attendees = new ArrayList( );
25 Person p = new Person( "Old Name" );
26 attendees.Add( p );
27
28 // Try to change the name:
29 // Would work if Person was a reference type.
30 Person p2 = (( Person )attendees[ 0 ] );
31 p2.Name = "New Name";
32
33 // Writes "Old Name":
34 Console.WriteLine(
35 attendees[ 0 ].ToString( ));
上述程式碼中,Person是一個值型別,在將其放入ArrayList時,會進行裝箱操作,這時會有一次複製操作,當我們需要獲得ArrayList內 Person物件的資訊時,需要一次拆箱,又會有一次複製操作,因此,當我們並沒有對ArrayList內的物件進行修改,而是針對副本進行修改。
我們可以通過以下的方式來修改上述程式碼存在的問題。
程式碼
1 public interface IPersonName
2 {
3 string Name
4 {
5 get; set;
6 }
7 }
8
9 struct Person : IPersonName
10 {
11 private string _Name;
12
13 public string Name
14 {
15 get
16 {
17 return _Name;
18 }
19 set
20 {
21 _Name = value;
22 }
23 }
24
25 public override string ToString( )
26 {
27 return _Name;
28 }
29 }
30
31 // Using the Person in a collection:
32 ArrayList attendees = new ArrayList( );
33 Person p = new Person( "Old Name" );
34 attendees.Add( p ); // box
35
36 // Try to change the name:
37 // Use the interface, not the type.
38 // No Unbox needed
39 (( IPersonName )attendees[ 0 ] ).Name = "New Name";
40
41 // Writes "New Name":
42 Console.WriteLine(
43 attendees[ 0 ].ToString( )); // unbox
44
45
裝箱後的引用型別實現了原來值型別物件上所有的介面,這意味著不會再發生複製,但是當我們呼叫IPersonName.Name屬性時,它會將呼叫請求轉發給“箱子”內部的值型別,在值型別上實現介面使我們可以訪問”箱子“的內部,從而允許直接改變ArrayList中的資訊。
總之,我們應該對任何將值型別轉換為System.Object或者介面型別的構造保持密切的關注,例如將值型別放入集合中,在值型別上呼叫 System.Object定義的方法等,這些操作都會將值型別轉換為System.Object,只要有可能,我們都應該避免這種轉換。
作者:李勝攀
出處:http://wing011203.cnblogs.com/
裝箱會將一個值型別放入一個未具名型別(untyped)的引用物件中,從而允許該值型別應用於那些只能使用引用型別的場合。拆箱則會從前面的裝箱物件中提取出一個值型別的副本。裝箱和拆箱都是比較耗時的操作。
裝箱操作會將值型別轉換為一個引用型別,這個過程中會建立一個新的引用獨享,然後將其分配到堆上,同時值型別的副本會被儲存在該引用物件內部。當我們需要從裝箱物件中獲取任何資訊時,會建立值型別的一個副本,然後將其返回。其中的關鍵是:當我們需要引用型別時,會建立一個新的引用型別的物件並將其放入到堆中;當我們需要訪問已經裝箱的物件資訊時,就會建立對應值型別的一個副本,並將其返回。
裝箱和拆箱最大的問題是它們會自動發生。當我們使用的是值型別,而期望的是引用型別,那麼編譯器就會自動產生裝箱和拆箱語句。
我們來看下面的語句,居然也發生了裝箱和拆箱操作。
Console.WriteLine("A few numbers:{0}, {1}, {2}",
25, 32, 50);
上述程式碼之所以發生了裝箱,是因為WriteLine方法需要的引數型別是System.Object,而25是一個int型別,屬於值型別,因此需要裝箱,而在WriteLine方法內部實現時,需要呼叫方法引數的ToString()方法,為了呼叫裝箱物件的方法,就會發生拆箱的操作。
為了避免裝箱和拆箱,可以將上述程式碼進行如下修改。
Console.WriteLine("A few numbers:{0}, {1}, {2}",
25.ToString(), 32.ToString(), 50.ToString());
另外,由於裝箱和拆箱都會產生新的例項,那麼有時會產生一些詭異的bug,我們來檢視下面的程式碼。
程式碼
1 public struct Person
2 {
3 private string _Name;
4
5 public string Name
6 {
7 get
8 {
9 return _Name;
10 }
11 set
12 {
13 _Name = value;
14 }
15 }
16
17 public override string ToString( )
18 {
19 Return _Name;
20 }
21 }
22
23 // Using the Person in a collection:
24 ArrayList attendees = new ArrayList( );
25 Person p = new Person( "Old Name" );
26 attendees.Add( p );
27
28 // Try to change the name:
29 // Would work if Person was a reference type.
30 Person p2 = (( Person )attendees[ 0 ] );
31 p2.Name = "New Name";
32
33 // Writes "Old Name":
34 Console.WriteLine(
35 attendees[ 0 ].ToString( ));
上述程式碼中,Person是一個值型別,在將其放入ArrayList時,會進行裝箱操作,這時會有一次複製操作,當我們需要獲得ArrayList內 Person物件的資訊時,需要一次拆箱,又會有一次複製操作,因此,當我們並沒有對ArrayList內的物件進行修改,而是針對副本進行修改。
我們可以通過以下的方式來修改上述程式碼存在的問題。
程式碼
1 public interface IPersonName
2 {
3 string Name
4 {
5 get; set;
6 }
7 }
8
9 struct Person : IPersonName
10 {
11 private string _Name;
12
13 public string Name
14 {
15 get
16 {
17 return _Name;
18 }
19 set
20 {
21 _Name = value;
22 }
23 }
24
25 public override string ToString( )
26 {
27 return _Name;
28 }
29 }
30
31 // Using the Person in a collection:
32 ArrayList attendees = new ArrayList( );
33 Person p = new Person( "Old Name" );
34 attendees.Add( p ); // box
35
36 // Try to change the name:
37 // Use the interface, not the type.
38 // No Unbox needed
39 (( IPersonName )attendees[ 0 ] ).Name = "New Name";
40
41 // Writes "New Name":
42 Console.WriteLine(
43 attendees[ 0 ].ToString( )); // unbox
44
45
裝箱後的引用型別實現了原來值型別物件上所有的介面,這意味著不會再發生複製,但是當我們呼叫IPersonName.Name屬性時,它會將呼叫請求轉發給“箱子”內部的值型別,在值型別上實現介面使我們可以訪問”箱子“的內部,從而允許直接改變ArrayList中的資訊。
總之,我們應該對任何將值型別轉換為System.Object或者介面型別的構造保持密切的關注,例如將值型別放入集合中,在值型別上呼叫 System.Object定義的方法等,這些操作都會將值型別轉換為System.Object,只要有可能,我們都應該避免這種轉換。
作者:李勝攀
出處:http://wing011203.cnblogs.com/
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-625129/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- c#的裝箱和拆箱C#
- Effective C#:儘量減少記憶體垃圾C#記憶體
- C#之拆箱,裝箱C#
- 深入理解C#的裝箱和拆箱C#
- Visual C#裝箱與拆箱C#
- java裝箱拆箱Java
- Java的自動裝箱和拆箱Java
- 談談JavaScript中裝箱和拆箱JavaScript
- 深入剖析Java中的裝箱和拆箱Java
- Java自動拆箱與裝箱Java
- c#之裝箱和取消裝箱C#
- 資料型別及拆箱裝箱資料型別
- 深入淺出瞭解“裝箱與拆箱”
- 深入理解Java之裝箱與拆箱Java
- Java 效能筆記:自動裝箱/拆箱Java筆記
- Java中的自動裝箱與拆箱Java
- 如何理解Java中的自動拆箱和自動裝箱?Java
- 【java】JDK5的新特性→→自動裝箱和拆箱JavaJDK
- Java語法糖2:自動裝箱和自動拆箱Java
- Java自動裝箱/拆箱 - Java那些事兒Java
- [JAVA] Java物件導向之包裝類,拆箱、裝箱Java物件
- 基礎鞏固、探尋Java裝箱和拆箱的奧妙!Java
- Java中的自動裝箱與自動拆箱Java
- dotnet學習筆記一 - 裝箱拆箱 (轉)筆記
- Java學習之自動裝箱和自動拆箱原始碼分析Java原始碼
- 通過原始碼瞭解Java的自動裝箱拆箱原始碼Java
- java中的內部類和自動拆裝箱Java
- .NET Core CSharp 中級篇 2-1 裝箱與拆箱CSharp
- Java 效能要點:自動裝箱/ 拆箱 (Autoboxing / Unboxing)Java
- .NET中的六個重要概念:棧、堆、值型別、引用型別、裝箱和拆箱型別
- Integer 自動拆箱封箱
- 【轉】.NET中的六個重要概念:棧、堆、值型別、引用型別、裝箱和拆箱型別
- js儘量減少程式碼重複執行的次數JS
- 高效能web建站規則(儘量減少http請求)WebHTTP
- java基礎(八) 深入解析常量池與裝拆箱機制Java
- 【JS迷你書】基本型別之拆箱操作JS型別
- 一文讀懂什麼是Java中的自動拆裝箱Java
- 設計模式——使用模板方法模式儘量減少重複相似的程式碼段設計模式