減少裝箱與拆箱

在 .NET 中存在一個的衝突,值類型不應該被設計爲多態類型,但是 .NET Framework 又必須把 System.Object 設計爲引用類型,並把它作爲整個對象體系的基礎。針對這一衝突 .NET 引入了裝箱與拆箱。所謂的裝箱就是把值類型放在非類型化的引用對象中,使得需要使用引用類型的地方也可以使用值類型,而拆箱指的是把已經裝箱的值複製出來一份。在只能使用 System.Object 類型或接口類型的地方使用值類型,那麼就必定設計到裝箱和拆箱操作。但是裝箱和拆箱操作嚴重的影響了所開發的應用程序的性能,並且在部分情況下還會創建對象的臨時拷貝,進而會造成難以查找的 bug 。下面我們就具體來講解一下如何減少裝箱和拆箱。

零、基本方法需要注意

裝箱操作會把值類型轉換爲引用類型,新創建的引用對象被分配在了堆上面,裏面包含了對原值的一個拷貝,而且還實現了值類型的所有接口,當有外部代碼查詢裏面的內容時,系統會將裏面的原值拷貝一份返回給調用方。這種拷貝僅僅是一次性的,下次再次查詢時會重新拷貝一份裏面的原值。在 .NET 2.0 以後我們可以使用泛型類型及其方法來取代大部分裝箱與拆箱操作,但是 .NET 中依然存在大量的方法接收 System.Object 類型的參數,因此在以值類型爲參數調用這些方法的時候依然會發生裝箱和拆箱操作。例如下面這段代碼就用到了裝箱:

int code=100;
Console.WriteLine($"我的數學成績是 {code}");

上面的代碼看似簡單,實際上系統進行了複雜的操作。首先系統會創建 System.Object 引用構成的數組,然後交給編譯器生成的方法去解析,同時因爲 code 是值類型的變量因此還需要進行裝箱操作。另外代碼中隱式的調用了 ToString() 方法,這個操作相當於在裝箱後的原值上調用。這些操作類似於如下的代碼:

int code = 100;
object o = code;
Console.WriteLine($"我的數學成績是 {o.ToString()}");

針對 Object 數組來創建字符串的方法會產生和下面這段代碼類似的邏輯來處理 Object :

object para =100;
object o=para;
int num = (int)o;
string output = num.ToString();

如果要避免上述問題,我們可以提前把值手動轉化爲 string 類型,也就是顯示的調用 ToString 方法,這樣就可以防止編譯器將其隱式的轉換爲 System.Object 。

int code=100;
Console.WriteLine($"我的數學成績是 {code.ToString()}");

避免裝箱第一原則:注意代碼中會隱式轉換成 System.Object 的位置,避免在不需要使用 System.Object 的地方直接使用值類型。

一、泛型方法需要注意

開發人員還可以使用泛型集合來避免拆箱和裝箱操作,但是這裏需要注意的是 .NET 第一次實現集合所保存的是指向 System.Object 實例的引用,如果在裏面放入值類型就會發生裝箱操作,從集合中移除時就會發生拆箱操作。下面我們來可一個簡單的例子:

public struct Student
{
    public string Name {get;set;}
    public override string ToString()
    {
        return Name;
    }
}
var students = new List<Student>();
Student s = new Student
{
    Name = "張三"
};
students.Add(s);
Student s1 = students[0];
s1.Name="李四";
Console.WriteLine(students[0].ToString());

在上述代碼中 Student 是值類型,因此即時編譯器會創建一個封閉泛型類型,這樣就可以讓 Student 對象可以以未裝箱的形式放入集合中。但是當我們從旁那個集合中取出一個對象時,取出的是這個對象的一個拷貝,因此當我們修改這個對象的 Name 屬性是實際上並不是修改的原來那個對象的 Name 屬性。當我們在 students[0] 上調用 ToString 方法時又創建了一份拷貝。因此這裏我建議將值類型設計爲不可變類型。

二、小結

值類型可以轉換爲指向 System.Object 或其他接口的引用,因爲這種轉換是默認發生的,因此產生錯誤後很難排查。並且把值類型當成多態中的類型還會影響程序的應能,因此需要注意把值類型轉換爲 System.Object 或其他接口的地方。

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