共計 3287 個字符,預計需要花費 9 分鐘才能閱讀完成。
C# 程序中怎么使用泛型集合代替非泛型集合,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
軟件開發過程中,不可避免會用到集合,C# 中的集合表現為數組和若干集合類。不管是數組還是集合類,它們都有各自的優缺點。如何使用好集合是我們在開發過程中必須掌握的技巧。不要小看這些技巧,一旦在開發中使用了錯誤的集合或針對集合的方法,應用程序將會背離你的預想而運行。
使用泛型集合代替非泛型集合
如果要讓代碼高效運行,應該盡量避免裝箱和拆箱,以及盡量減少轉型。很遺憾,在微軟提供給我們的第一代集合類型中沒有做到這一點,下面我們看 ArrayList 這個類的使用情況:
ArrayList al=new ArrayList(); al.Add(0); al.Add(1); al.Add(mike foreach (var item in al) { Console.WriteLine(item); }
上面這段代碼充分演示了我們可以將程序寫得多么糟糕。
首先,ArrayList 的 Add 方法接受一個 object 參數,所以 al.Add(1) 首先會完成一次裝箱;其次,在 foreach 循環中,待遍歷到它時,又將完成一次拆箱。
在這段代碼中,整形和字符串作為值類型和引用類型,都會先被隱式地強制轉型為 object,然后在 foreach 循環中又被轉型回來。
同時,這段代碼也是非類型安全的:我們然 ArrayList 同時存儲了整型和字符串,但是缺少編譯時的類型檢查。雖然有時候需要有意這樣去實現,但是更多的時候,應該盡量避免。缺少類型檢查,在運行時會帶來隱含的 Bug。集合類 ArrayList 如果進行如下所示的運算,就會拋出一個 IvalidCastException:
ArrayList al=new ArrayList(); al.Add(0); al.Add(1); al.Add(mike int t = 0; foreach (int item in al) { t += item; }
ArrayList 同時還提供了一個帶 ICollection 參數的構造方法,可以直接接收數組,如下所示:
var intArr = new int[] {0, 1, 2, 3};ArrayList al=new ArrayList(intArr);
該方法內部實現一樣糟糕,如下所示(構造方法內部最終調用了下面的 InsertRange 方法):
public virtual void InsertRange(int index, ICollection c){ if (c == null) { throw new ArgumentNullException( c , Environment.GetResourceString( ArgumentNull_Collection)); } if ((index 0) || (index this._size)) { throw new ArgumentOutOfRangeException( index , Environment.GetResourceString( ArgumentOutOfRange_Index)); } int count = c.Count; if (count 0) { this.EnsureCapacity(this._size + count); if (index this._size) { Array.Copy(this._items, index, this._items, index + count, this._size - index); } object[] array = new object[count]; c.CopyTo(array, 0); array.CopyTo(this._items, index); this._size += count; this._version++; }}
概括來講,如果對大型集合進行循環訪問、轉型或裝箱和拆箱操作,使用 ArrayList 這樣的傳統集合對效率影響會非常大。鑒于此,微軟提供了對泛型的支持。泛型使用一對 括號將實際類型括起來,然后編譯器和運行時會完成剩余的工作。微軟也不建議大家使用 ArrayList 這樣的類型了,轉而建議使用它們的泛型實現,如 List T。
注意,非泛型集合在 System.Collections 命名空間下,對應的泛型集合則在 System.Collections.Generic 命名空間下。
建議一開始的那段代碼的泛型實現為:
List int intList = new List int intList.Add(1); intList.Add(2); //intList.Add(mike foreach (var item in intList) { Console.WriteLine(item); }
代碼中被注釋的那一行不會被編譯通過,因為“mike 不是整型,這里就體現了類型安全的特點。
下面比較了非泛型集合和泛型集合在運行中的效率:
static void Main(string[] args) { Console.WriteLine( 開始測試 ArrayList: TestBegin(); TestArrayList(); TestEnd(); Console.WriteLine(開始測試 List T : TestBegin(); TestGenericList(); TestEnd(); } static int collectionCount = 0; static Stopwatch watch = null; static int testCount = 10000000; static void TestBegin() { GC.Collect(); // 強制對所有代碼進行即時垃圾回收 GC.WaitForPendingFinalizers(); // 掛起線程,執行終結器隊列中的終結器(即析構方法) GC.Collect(); // 再次對所有代碼進行垃圾回收,主要包括從終結器隊列中出來的對象 collectionCount = GC.CollectionCount(0); // 返回在 0 代碼中執行的垃圾回收次數 watch = new Stopwatch(); watch.Start(); } static void TestEnd() { watch.Stop(); Console.WriteLine(耗時: + watch.ElapsedMilliseconds.ToString()); Console.WriteLine(垃圾回收次數: + (GC.CollectionCount(0) - collectionCount)); } static void TestArrayList() { ArrayList al = new ArrayList(); int temp = 0; for (int i = 0; i testCount; i++) { al.Add(i); temp = (int)al[i]; } al = null; } static void TestGenericList() { List int listT = new List int int temp = 0; for (int i = 0; i testCount; i++) { listT.Add(i); temp = listT[i]; } listT = null; }
輸出為:
開始測試 ArrayList:
耗時:2375
垃圾回收次數:26
開始測試 List T :
耗時:220
垃圾回收次數:5
看完上述內容,你們掌握 C# 程序中怎么使用泛型集合代替非泛型集合的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注丸趣 TV 行業資訊頻道,感謝各位的閱讀!