如果程式碼中會造成編譯器的反覆裝箱,可改為手動裝箱,這樣來使程式碼執行更快,看下面程式碼:
//手动装箱 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實參是指向堆上的一個物件的指標。
此外,將值類型的一個未裝箱實例轉換為類型的某個介面是,要求實例進行裝箱。因為介面變數必須包含對堆上的一個物件的參考。看下面程式碼:
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); } }
以上就是C#基礎知識整理 基礎知識(19) 值類型的裝箱和拆箱(二)的內容,更多相關內容請關注PHP中文網(www.php.cn)!