前言:
此系列都為個人對C#的回顧,屬於個人理解,新司機可參考、求老司機指點。如果有什麼問題或不同見解,歡迎大家與我溝通!
目錄:
泛型是什麼
泛型的好處及用途
如何聲明使用泛型
泛型類
泛型方法
泛型接口
泛型委托
泛型約束
通過“參數化類型(不指定類型)”來實現在同一份代碼上操作多種數據類型。
在聲明時不指定類型,編譯時生成為占位符“`1”(1表示只有一個泛型參數,兩個就是`2,以此類推) ,調用時會生成不同類型的方法,也就是最終還是會生成多個方法!
我們來假設一個場景:需要在控制台輸出各種類型(可能不太形象,個人感覺ORM框架中數據保存那塊很形象)。
在泛型沒出現之前,我們基本都是采用以下這種方式來實現:
這個時候該有人說了這樣寫以後擴展太麻煩,咱可以采用object,畢竟object是所有類型的基類,也就是以下這種:
確實,類型不明確的地方可以使用object類型,一樣可以達到目的。不過這種辦法會引起:
1.使用object導致的類型安全問題
2.拆箱裝箱導致性能下降
泛型的出現就是為了解決以上幾種情況的問題,上面那個例子可以改為:
綜其上述,泛型的好處有:
1.泛型采用延遲聲明思想,將“參數化類型”將類型抽象化,從而實現更為靈活的復用。
2.泛型賦予了代碼更強的類型安全,更高的效率,更清晰的約束。
泛型的用途很廣泛,在.net各處都有體現,比如常見的List<T>、IEnumerable<T>、ICollection<T>等等。個人覺得說起泛型就應該說說委托。。。
例如:Linq中的方法都是采用的泛型加委托
泛型類:
1 /// <summary> 2 /// 這就是一個泛型類,是不是很簡單 3 /// </summary> 4 /// <typeparam name="T">類型參數</typeparam> 5 public class Generic<T> 6 { 7 /// <summary> 8 /// 泛型方法 9 /// </summary> 10 /// <param name="type">泛型參數,根據類指定</param> 11 /// <returns></returns> 12 public T OutPut(T type) 13 { 14 Console.WriteLine(type.GetType()); 15 return default(T); 16 } 17 } 18 19 public class Test 20 { 21 Generic<int> genericInt; 22 Generic<string> genericString; 23 public Test() 24 { 25 //泛型類調用1 26 genericInt = new Generic<int>(); 27 genericInt.OutPut(1); 28 //泛型類調用2 29 genericString = new Generic<string>(); 30 genericString.OutPut("我就是泛型方法,不過我的參數類型是根據類來決定的,我的兄弟會在下面粗現~~"); 31 } 32 }
泛型方法:
5 public class Generic<T> 6 { 7 /// <summary> 8 /// 泛型方法 9 /// </summary> 10 /// <param name="type">泛型參數,根據類指定</param> 11 /// <returns></returns> 12 public T OutPut(T type) 13 { 14 Console.WriteLine(type.GetType()); 15 return default(T);//default(T)返回類型默認值 16 } 17 18 /// <summary> 19 /// 泛型方法,注意看,我與上面不同哦!調用也不同哦! 20 /// </summary> 21 /// <typeparam name="TResult"></typeparam> 22 /// <param name="tresult"></param> 23 /// <returns></returns> 24 public TResult GenericAction<TResult>(TResult tresult) 25 { 26 Console.WriteLine(tresult.GetType()); 27 return default(TResult); 28 } 29 /// <summary> 30 /// 可以隨便寫,類上面也一樣~~~~~ 31 /// </summary> 32 public TResult GenericAction<TResult, LResult, SResult>(TResult tresult,LResult lresult,SResult sresult) 33 { 34 Console.WriteLine(tresult.GetType()); 35 return default(TResult); 36 } 37 } 38 39 public class Test 40 { 41 Generic<int> genericInt; 42 Generic<string> genericString; 43 Generic<DateTime> genericDateTime; 44 public Test() 45 { 46 //泛型類調用1 47 genericInt = new Generic<int>(); 48 genericInt.OutPut(1); 49 //泛型類調用2 50 genericString = new Generic<string>(); 51 genericString.OutPut("我就是泛型方法,不過我的參數類型是根據類來決定的,我的兄弟會在下面粗現~~"); 52 //泛型方法調用3 53 genericDateTime = new Generic<DateTime>(); 54 genericDateTime.GenericAction<string>("我的最終輸出結果是根據方法的類型參數決定的,跟類無關!"); 55 } 56 } 57 }
泛型接口:
1 //泛型接口 2 public interface IGeneric<T> 3 { 4 } 5 //普通類繼承泛型接口,需要指定基類泛型類型 6 public class Generic : IGeneric<string> 7 { 8 } 9 //泛型類繼承泛型接口 10 public class Test<T> : IGeneric<int> 11 { 12 } 13 //泛型類繼承泛型接口,並運用子類的泛型類型 14 public class Test1<T> : IGeneric<T> 15 { 16 } 17 //隨便怎麼玩都可以~~~~~~~其他的大家都可以試試
泛型委托:個人覺得泛型委托是個重點,在.net中處處體現了這點,比如我上面所說到的Linq方法。如果有對委托不熟悉的,我會在後面寫一篇關於對委托的介紹。
1.首先我們先自定義一個泛型委托
1 class Program 2 { 3 //這裡定義了一個無參有返回值的泛型的委托 4 public delegate T CustomDelegate<T>(); 5 //這裡定義了一個有參有返回值的泛型的委托 6 public delegate T CustomDelegate1<T>(T type); 7 8 static void Main(string[] args) 9 { 10 //聲明這個無參有返回值泛型委托 11 CustomDelegate<int> customDelegate = new CustomDelegate<int>(() => { return 1; });//() => { return 1; }匿名方法 12 //調用這個泛型委托 13 customDelegate.Invoke(); 14 15 //聲明這個有參有返回值泛型委托 16 CustomDelegate1<string> customDelegate1 = new CustomDelegate1<string>((i) => { return i; }); 17 customDelegate1.Invoke("有參有返回值"); 18 19 //其他的無參無返回值,有參無返回值大家都可以試下,也可以嘗試定義成多個參數的委托試試!!! 20 } 21 }
2.微軟在.net為我們封裝好的三個泛型委托,為了簡化咱們的工作量,不用自定義委托
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //1.Func 6 Func<string> func = new Func<string>(() => { return default(string); }); 7 8 //2.Action 9 Action<string> action = new Action<string>((i) => { }); 10 11 //3.Predicate 12 Predicate<bool> pre = new Predicate<bool>((i) => { return true; }); 13 14 //這三個泛型委托用處不同. 15 //比如Func就在Linq方法中經常用到,在F12進去之後可以看到類型參數上帶有out修飾符 16 //Action,在F12進去之後可以看到類型參數上帶有in修飾符 17 //in 與 out 則就是我們後面要說的逆變與協變了 18 //Predicate,則就是一個條件判斷委托了 19 //具體的應用場景大家可以想象下 20 } 21 }
泛型約束:
在定義泛型類時,可以對客戶端代碼能夠在實例化類時用於類型參數的類型種類施加限制。如果客戶端代碼嘗試使用某個約束所不允許的類型來實例化類,則會產生編譯時錯誤。這些限制稱為約束。約束是使用 where 上下文關鍵字指定的。下表列出了六種類型的約束:
T:結構
類型參數必須是值類型。可以指定除 Nullable 以外的任何值類型。有關更多信息,請參見使用可空類型(C# 編程指南)。
T:類
類型參數必須是引用類型,包括任何類、接口、委托或數組類型。
T:new()
類型參數必須具有無參數的公共構造函數。當與其他約束一起使用時,new() 約束必須最後指定。
T:<基類名>
類型參數必須是指定的基類或派生自指定的基類。
T:<接口名稱>
類型參數必須是指定的接口或實現指定的接口。可以指定多個接口約束。約束接口也可以是泛型的。
T:U
為 T 提供的類型參數必須是為 U 提供的參數或派生自為 U 提供的參數。這稱為裸類型約束。
1.T:結構
2.T:類
3.T:new()
4.T:<基類名>
5.T:<接口名稱>
6.T:U
擴展:
讓我們通過泛型與泛型委托來擴展一個IEnumerable方法(需要了解C#擴展方法)
1 public static class Extends //靜態類 2 { 3 /// <summary> 4 /// 只要有一個滿足於predicate條件就返回true 5 /// </summary> 6 /// <typeparam name="TSource"></typeparam> 7 /// <param name="tsource"></param> 8 /// <param name="predicate"></param> 9 /// <returns></returns> 10 public static bool MaxBool<TSource>(this IEnumerable<TSource> tsource, Func<TSource, bool> predicate) 11 { 12 foreach (var item in tsource) 13 { 14 if (predicate(item)) 15 { 16 return true; 17 } 18 } 19 return false; 20 } 21 } 22 23 24 static void Main(string[] args) 25 { 26 //為什麼List能使用MaxBool()我就不用說了吧。。。。。 27 List<int> listA = new List<int>() { 1, 2, 3 }; 28 bool trueOrFalse = listA.MaxBool(item => item > 20); 29 }
這裡只是一個簡單的例子,大家可以試試對其他的進行擴展!