1. 程式人生 > >【轉】編寫高質量代碼改善C#程序的157個建議——建議16:元素數量可變的情況下不應使用數組

【轉】編寫高質量代碼改善C#程序的157個建議——建議16:元素數量可變的情況下不應使用數組

system sed 維數 優化 高質量 watch 擴展方法 calling 64 bit

建議16:元素數量可變的情況下不應使用數組

在C#中,數組一旦被創建,長度就不能改變。如果我們需要一個動態且可變長度的集合,就應該使用ArrayList或List<T>來創建。 而數組本身,尤其是一維數組,在遇到要求高效率的算法時,則會專門被優化以提升其效率。一維數組也成為向量,其性能是最佳的,在IL中使用了專門的指令來 處理它們(如newarr、ldelem、ldelema、ldelen和stelem)。

從內存的使用角度來講,數組在創建時被分配了一段固定長度的內存。如果數組的元素是值類型,則每個元素的長度等於相應的值類型的長度;如果數組的元素是引用類型,則每個元素的長度為該引用類型的IntPtr.Size(IntPtr:It‘s a class that wraps a pointer that is used when calling Windows API functions. The underlying pointer may be 32 bit or 64 bit, depending on the platform.)。數組的存儲結構一旦被分配,就不能再變化。而ArrayList是鏈表結構,可以動態的增減內存空間,如果ArrayList存儲的是值類型,則會為每個元素增加12字節的空間,其中4個字節擁有對象引用,8字節是元素裝箱時引入的對象頭。List<T>是ArrayList的泛型實現,它省去了裝箱和拆箱帶來的開銷。

註意:

由於數組本身在內存上的特點,因此在使用數組的過程中還應該註意大對象的問題。所謂“大對象”,是指那些占內存超過85000字節的對象,它們被分配在大對象堆裏。大對象的分配和回收和小對象都不太一樣,尤其是回收,大對象在回收過程中會帶來效率很低的問題。所以,不能對數組指定過大的長度,這會讓數組成為一個大對象。

如果一定要動態改變數組的長度,一種方法是將數組轉換為ArrayList或List<T>,如下面代碼說是:

            int[] iArr = { 0, 1, 2, 3, 4, 5, 6 };
            ArrayList arrayListInt 
= new ArrayList(iArr); //將數組轉變為ArrayList arrayListInt.Add(7); List<int> listInt = iArr.ToList<int>(); //將數組轉變為List<T> listInt.Add(7);

還有一種方法是數組的復制功能。數組繼承自System.Array,抽象類System.Array提供了一些有用的實現方法。其中就包括Copy方法,它負責將一個數組的內容復制到另外一個數組中。無論哪種方法,改變數組長度就相當於重新創建了一個數組對象。

為了讓數組看上去本身就具有動態改變長度的功能,可以創建一個名為Resize的擴展方法,代碼如下所示:

    public static class ClassForExtensions
    {
        public static Array ReSize(this Array array, int newSize)
        {
            Type t = array.GetType().GetElementType();
            Array newArray = Array.CreateInstance(t, newSize);
            Array.Copy(array, 0, newArray, 0, Math.Min(array.Length, newSize));
            return newArray;
        }
    }

調用方法看起來如下:

int[] iArr = { 0, 1, 2, 3, 4, 5, 6 };
iArr = (int[])iArr.ReSize(10);

下面對改變數組長度和改變Lisit<T>長度的耗時做一個比較,以便強調本建議的主題:在元素數量可變的情況下不應該使用數組。

        private static void ResizeArray()
        {
            int[] iArr = { 0, 1, 2, 3, 4, 5, 6 };
            Stopwatch watch = new Stopwatch();
            watch.Start();
            iArr = (int[])iArr.ReSize(10);
            watch.Stop();
            Console.WriteLine("ResizeArray: " + watch.Elapsed);
        }

        private static void ResizeList()
        {
            List<int> iArr = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });
            Stopwatch watch = new Stopwatch();
            watch.Start();
            iArr.Add(0);
            iArr.Add(0);
            iArr.Add(0);
            watch.Stop();
            Console.WriteLine("ResizeList: " + watch.Elapsed);
        }

輸出為:

ResizeArray:00:00:00.0004441

ResizeList:00:00:0:0000036

當然,嚴格意義上講,List<T>不存在改變長度的說法,本建議只是為了比較,將iArr的長度變為10,同時還進行了賦值。即便這樣,我們可以看到,在時間效率上ResizeList比ResizeArray要高100倍以上。

轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技

【轉】編寫高質量代碼改善C#程序的157個建議——建議16:元素數量可變的情況下不應使用數組