今天我們來學習在C#的泛型技巧,傳統的課本都在講解什么是泛型,然后列舉一大堆代碼示例告訴你什么是泛型,今天我們就來聊聊更加本質的東西,我為什么要用泛型?它是來解決什么問題的?底層原理是什么?
簡單來說,泛型解決的是什么問題呢?算法重用和提升性能的。
最最經典的例子是什么?就是微軟支持的List<T>類型,想必絕大多的程序員都是使用過這個類的。如果沒有這個類,我們可以想象下,如果要你開發一個算法類,支持對數組的長度動態擴展的,還支持一些普遍的數組操作的話。你會怎么寫?
比如我要寫 int 數據類型的數組操作功能,寫了一遍的 ListInt 類,如果這時候我們需要寫一個 float 類型的數組操作對象,我們又得寫一遍 ListFloat, 也就是說,每用到一個新的類型的時候,都需要寫一遍類,而這之間的代碼絕大部分都是相似的。
你可能會提出我搞一個 ListObject 類就可以了,這樣就可以滿足所有的情況了,恭喜你,這真的是個很不錯的想法,無論你的類型是什么?都會轉換為object進行數組的操作,這樣只要寫一套代碼就可以了。只是,當你用了一段時間之后,它會碰到2個比較麻煩的問題
1. 我實例化了 ListObject 類,Add了很多了int類型的數據,我現在要獲取所有的int類型的數組,比如int[],這個比較麻煩,需要對所有object數據進行強制轉換。數組的長度比較大的時候,這時候性能就很差,為什么說c和c++性能高,都是地址基於地址的操作,沒有類型轉換一說。
2. 我實例化了 ListObject 類,Add了很多了int類型的數據,但是我在用的時候,很容易就誤以為是float類型的,從而轉換失敗,降低了開發的效率。
這時候我們就要讓泛型出場了,List<T>類型,將數組一般的操作邏輯都進行了封裝,add,remove,insert,clear,等等操作。當你需要使用int類型的時候,就可以定義List<int>的對象,當你需要short類型的時候,就可以定義List<short>對象,而且不用再轉換來轉換去了。
似乎除了上述的情況需要泛型外,似乎我們實際中已經不需要泛型了,答案當然是否定的、
假設,我們需要編寫一個公共的方法。是對數組操作的,比如我們會寫一個方法,將數組擴充到指定長度
/// <summary> /// 將一個數組進行擴充到指定長度,或是縮短到指定長度 -> /// Extend an array to a specified length, or shorten to a specified length or fill /// </summary> /// <typeparam name="T">數組的類型</typeparam> /// <param name="data">原先數據的數據</param> /// <param name="length">新數組的長度</param> /// <returns>新數組長度信息</returns> public static T[] ArrayExpandToLength<T>( T[] data, int length ) { if (data == null) return new T[length]; if (data.Length == length) return data; T[] buffer = new T[length]; Array.Copy( data, buffer, Math.Min( data.Length, buffer.Length ) ); return buffer; }
這么來看,這個就特別是否寫成泛型,和類型無關的情況。
之前是數組的例子,我們再來看看另一個實際的例子。我們先看看代碼:
/// <summary> /// 操作結果的泛型類,允許帶一個用戶自定義的泛型對象,推薦使用這個類 /// </summary> /// <typeparam name="T">泛型類</typeparam> public class OperateResult<T> : OperateResult { #region Constructor /// <summary> /// 實例化一個默認的結果對象 /// </summary> public OperateResult( ) : base( ) { } /// <summary> /// 使用指定的消息實例化一個默認的結果對象 /// </summary> /// <param name="msg">錯誤消息</param> public OperateResult( string msg ) : base( msg ) { } /// <summary> /// 使用錯誤代碼,消息文本來實例化對象 /// </summary> /// <param name="err">錯誤代碼</param> /// <param name="msg">錯誤消息</param> public OperateResult( int err, string msg ) : base( err, msg ) { } #endregion /// <summary> /// 用戶自定義的泛型數據 /// </summary> public T Content { get; set; } }
當你的自定義類需要攜帶一個數據時,而這個數據可能是任意類型的時候,這時候可以使用泛型。比如這里的 OperateResult<int> 就是非常好的例子。可以用來攜帶任意的自定義的數據。甚至是數組
OperateResult<List<int>> 這樣也是可以的。
未完待與