如果代碼中會造成編譯器的反復裝箱,可改為手動裝箱,這樣來使代碼執行更快,看下面代碼:
[csharp]
//手動裝箱
Int32 v = 5;
//由於string.Format的參數是object類型,所以這裡會造成三次裝箱。
Console.WriteLine(string.Format("{0},{1},{2}", v, v, v));
//修改一下,當然這只是一個小技巧,比如程序中對同一個值的同一個操作執行多次,
//應該都是要先執行一次,然後再使用的。
Object o = v;//裝箱一次
Console.WriteLine(string.Format("{0},{1},{2}", o, o, o));
//手動裝箱
Int32 v = 5;
//由於string.Format的參數是object類型,所以這裡會造成三次裝箱。
Console.WriteLine(string.Format("{0},{1},{2}", v, v, v));
//修改一下,當然這只是一個小技巧,比如程序中對同一個值的同一個操作執行多次,
//應該都是要先執行一次,然後再使用的。
Object o = v;//裝箱一次
Console.WriteLine(string.Format("{0},{1},{2}", o, o, o));
通過前面的代碼段,我們再寫程序時就很容易判斷什麼時候值類型要裝箱。無非就是當要獲取一個值類型的引用時,就要裝箱。這裡也很清楚的可以看出值類型與引用類型的區別:
1、值類型不在托管堆中分配空間;而引用類型在實例化後就在堆上分配了類中指定的成員的的空間。
2、值類型沒有堆上的對象的額外成員,即“類型對象指針”、“同步索引”。
未裝箱的值類型沒有同步索引,因此不能使用該類型所在類的方法(比如lock)讓多個線程同步對這個實例的訪問。
雖然未裝箱的值類型沒有類型對象指針,但仍可調用由類型繼承或重寫的虛方法,比如Equals,GetHashCode,ToString。如果值類型重寫了其中任何一個虛方法,那麼CLR可以非虛地調用該方法,因為值類型是隱式密封的,沒有任何類型能夠從它派生。此外,用於調用虛方法的值類型實例不會被裝箱。如果重寫的虛方法要調用方法在基類中的實現,那麼在調用基類的實現時,值類型實例會裝箱。因為這些方法是有System.Object定義的,所以這些方法期望this實參是指向堆上的一個對象的指針。
此外,將值類型的一個未裝箱實例轉型為類型的某個接口是,要求實例進行裝箱。因為接口變量必須包含對堆上的一個對象的引用。看下面代碼:
[csharp]
class Program
{
static void Main(string[] args)
{
Point p1 = new Point(10, 10);
Point p2 = new Point(20, 20);
//調用ToString不裝箱,這裡ToString是一個虛方法
Console.WriteLine(p1.ToString());
//GetType是一個非虛方法,p1要裝箱
Console.WriteLine(p1.GetType());
//這裡調用的是public int CompareTo(Point p)
//p2不會裝箱
Console.WriteLine(p1.CompareTo(p2));
//p1要裝箱,這就是將未裝箱的值類型轉為類型的某個接口時
IComparable c = p1;
Console.WriteLine(c.GetType());
//這裡調用的是public Int32 CompareTo(Object o),
//而且c本來就是一個引用,因此不裝箱了
Console.WriteLine(p1.CompareTo(c));
//這裡調用的是c的CompareTo方法,參數是object型的
//所以要對p2裝箱
Console.WriteLine(c.CompareTo(p2));
//對c拆箱,並復制值到p2中
p2 = (Point)c;
Console.WriteLine(p2.ToString());
}
}
internal struct Point : IComparable
{
private Int32 x;
private Int32 y;
public Point(Int32 x, Int32 y)
{
this.x = x;
this.y = y;
}
public override string ToString()
{
return string.Format("{0},{1}", x, y);//這裡裝箱兩次,不知道有沒好辦法。
}
public int CompareTo(Point p)
{
return Math.Sign(Math.Sqrt(x * x + y * y) - Math.Sqrt(p.x * p.x + p.y * p.y));
}
public Int32 CompareTo(Object o)
{
if (GetType() != o.GetType())
{
throw new ArgumentException("o is not Point.");
}
return CompareTo((Point)o);
}
}