泛型是.net 2.0就有的特性,泛型在我們的平常的開發過程中使用得也非常多,為了更深刻地理解泛型,這篇文章就來總結一下。 什麼是泛型 可以用下麵2點來概括: 1,首先泛型也是一種類型(可以通過IL代碼看出來)。 2,泛型提供了類型參數化的能力,允許用不同的類型進行實例化,可以簡單地理解為:泛型是類 ...
泛型是.net 2.0就有的特性,泛型在我們的平常的開發過程中使用得也非常多,為了更深刻地理解泛型,這篇文章就來總結一下。
什麼是泛型
可以用下麵2點來概括: 1,首先泛型也是一種類型(可以通過IL代碼看出來)。 2,泛型提供了類型參數化的能力,允許用不同的類型進行實例化,可以簡單地理解為:泛型是類型的模板。為什麼要使用泛型
下麵通過一個demo來演示不使用泛型和使用泛型的實現,進而理解為什麼要使用泛型。
不使用泛型:
1 /// <summary> 2 /// 普通方法,使用ArrayList 3 /// </summary> 4 public static void MyNoGenericMethod() 5 { 6 ArrayList arrayList = new ArrayList(); 7 arrayList.Add(1);//產生裝箱 8 arrayList.Add(2);//產生裝箱 9 arrayList.Add("3");//編譯時沒有提示錯誤 10 11 var result = 0; 12 foreach (int item in arrayList) 13 { 14 result += item;//運行時錯誤 15 } 16 }
使用泛型:
1 /// <summary> 2 /// 泛型,使用List<T> 3 /// </summary> 4 /// <returns></returns> 5 public static void MyGenericMethod() 6 { 7 List<int> list = new List<int>(); 8 list.Add(1); 9 list.Add(2); 10 //list.Add("3");//編譯時就提示錯誤 11 12 var result = 0; 13 foreach (int item in list) 14 { 15 result += item; 16 } 17 }
從上面這個例子可以看到,使用泛型一是保證了類型安全,二是提高了性能,因為避免了裝箱和拆箱。
我們再來看一個例子,實現了一個int類型的棧,如下代碼:
1 /// <summary> 2 /// int類型的Stack 3 /// </summary> 4 public class MyIntStack 5 { 6 int stackPointer = 0; 7 int[] stackArray; 8 9 /// <summary> 10 /// 入棧 11 /// </summary> 12 /// <param name="x"></param> 13 public void Push(int x) 14 { 15 16 } 17 18 /// <summary> 19 /// 出棧 20 /// </summary> 21 /// <returns></returns> 22 public int Pop() 23 { 24 25 } 26 }
假如要將數據類型改成float,就需要將代碼copy一份並且將數據類型改成float,這樣代碼就重覆了,時間長了很難維護,那有沒有一種方法不需要copy代碼呢?
假如引入了泛型,就不需要重覆copy代碼了,如下代碼:
1 /// <summary> 2 /// 泛型的stack 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public class MyStack<T> 6 { 7 int stackPointer = 0; 8 T[] stackArray; 9 10 /// <summary> 11 /// 入棧 12 /// </summary> 13 /// <param name="x"></param> 14 public void Push(T x) 15 { 16 17 } 18 19 /// <summary> 20 /// 出棧 21 /// </summary> 22 /// <returns></returns> 23 public T Pop() 24 { 25 26 } 27 }
這樣,下次再換成double類型都沒有問題,所以這裡體現了泛型帶來的,代碼復用的好處。
所以,綜合上面兩個例子,我們可以總結出,使用泛型可以帶來以下好處:
1,代碼(演算法)復用。
2,類型安全。
3,提高性能,避免了裝箱和拆箱。
4,擴展性。
泛型的本質
反編譯上面示例的IL代碼,如下圖:從上圖的IL代碼可以看出:
1,泛型其實也是一個類(class)。
2,泛型類型生成IL代碼後跟普通類型的區別是,使用了占位符'<T>'。
泛型類型
聲明泛型類型跟聲明普通類型差不多,不同的地方在於,泛型在類名後面放一對尖括弧,並且使用了類型參數。 泛型類型的創建過程如下圖。聲明泛型類型:
1 /// <summary> 2 /// 聲明泛型類 3 /// </summary> 4 /// <typeparam name="T1"></typeparam> 5 /// <typeparam name="T2"></typeparam> 6 public class MyGenericClass<TRequest, TResponse> 7 where TRequest : class 8 where TResponse : class 9 { 10 11 }
創建構造類型和實例:
1 //創建構造類型和實例 2 var item = new MyGenericClass<Request, Response>();
完整代碼:
1 namespace ConsoleApplication11 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //創建構造類型和實例 8 var item = new MyGenericClass<Request, Response>(); 9 10 } 11 } 12 13 /// <summary> 14 /// 聲明泛型類 15 /// </summary> 16 /// <typeparam name="T1"></typeparam> 17 /// <typeparam name="T2"></typeparam> 18 public class MyGenericClass<TRequest, TResponse> 19 where TRequest : class 20 where TResponse : class 21 { 22 23 } 24 25 public class Request 26 { 27 28 } 29 30 public class Response 31 { 32 33 } 34 }View Code
泛型方法
泛型方法可以在泛型和非泛型類以及結構和介面中聲明,如下圖。1,聲明泛型方法
聲明泛型方法要註意兩點,封閉在圓括弧里的方法參數,和封閉在尖括弧里的類型參數,並且可以在方法參數後面放可選的約束子句。
如下代碼:
1 /// <summary> 2 /// 泛型方法 3 /// </summary> 4 /// <typeparam name="T">類型參數</typeparam> 5 /// <param name="input">方法參數</param> 6 public static void MyGenericMethod<T>(T input) where T : class 7 { 8 9 }
2,調用泛型方法
1 var rq= new Request(); 2 MyGenericMethod<Request>(rq);//原始調用
3,推斷類型
因為編譯器可以從我們傳入的方法參數中推斷出類型參數,所以有了推斷類型我們的調用更簡單,如下代碼:
1 MyGenericMethod(rq);//推斷類型
完整代碼如下:
1 namespace ConsoleApplication13 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //泛型方法 8 var rq= new Request(); 9 MyGenericMethod<Request>(rq);//原始調用 10 11 MyGenericMethod(rq);//推斷類型 12 13 } 14 15 /// <summary> 16 /// 泛型方法 17 /// </summary> 18 /// <typeparam name="T">類型參數</typeparam> 19 /// <param name="input">方法參數</param> 20 public static void MyGenericMethod<T>(T input) where T : class 21 { 22 23 } 24 25 } 26 27 public class Request 28 { 29 30 } 31 }View Code
泛型介面
泛型介面的聲明跟普通介面的聲明差不多,不同的地方在於,泛型介面需要在介面名稱之後的尖括弧中有類型參數。 1,聲明泛型介面 如下代碼:1 /// <summary> 2 /// 泛型介面 3 /// </summary> 4 /// <typeparam name="T">類型參數</typeparam> 5 public interface IMyGenericInterface<T> 6 { 7 void ReturnIt(T input); 8 9 }
2,實現泛型介面
1 /// <summary> 2 /// 泛型介面實現 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public class MyGenericInterfaceImpl<T> : IMyGenericInterface<T> where T:class 6 { 7 public void ReturnIt(T input) 8 { 9 10 } 11 }
3,完整調用
1 namespace ConsoleApplication14 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 var rq = new Request(); 8 var m = new MyGenericInterfaceImpl<Request>(); 9 m.ReturnIt(rq); 10 11 } 12 } 13 14 /// <summary> 15 /// 泛型介面 16 /// </summary> 17 /// <typeparam name="T">類型參數</typeparam> 18 public interface IMyGenericInterface<T> 19 { 20 void ReturnIt(T input); 21 22 } 23 24 /// <summary> 25 /// 泛型介面實現 26 /// </summary> 27 /// <typeparam name="T"></typeparam> 28 public class MyGenericInterfaceImpl<T> : IMyGenericInterface<T> where T:class 29 { 30 public void ReturnIt(T input) 31 { 32 33 } 34 } 35 36 public class Request 37 { 38 39 } 40 }View Code
泛型委托
泛型委托的聲明跟普通委托的聲明也差不多,不同的點在於,泛型委托包括委托形參和類型參數。 1,聲明泛型委托 如下代碼:1 namespace ConsoleApplication15 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //調用泛型委托 8 var myGenericDel = new MyGenericDel<string>(Simple.PrintString); 9 myGenericDel += Simple.PrintUpperString; 10 11 myGenericDel("Hello generic delegate!"); 12 } 13 } 14 15 /// <summary> 16 /// 聲明泛型委托 17 /// </summary> 18 /// <typeparam name="T"></typeparam> 19 /// <param name="input"></param> 20 public delegate void MyGenericDel<T>(T input); 21 22 public class Simple 23 { 24 /// <summary> 25 /// 聲明方法匹配委托 26 /// </summary> 27 /// <param name="input"></param> 28 public static void PrintString(string input) 29 { 30 Console.WriteLine(input); 31 } 32 33 /// <summary> 34 /// 聲明方法匹配委托 35 /// </summary> 36 /// <param name="input"></param> 37 public static void PrintUpperString(string input) 38 { 39 Console.WriteLine(input.ToUpper()); 40 } 41 42 } 43 }
2,Linq與泛型委托 說起泛型委托,就不得不提Linq中的泛型委托,.net 3.5為我們帶來了Linq和lambda表達式。 這三個泛型委托在linq中經常用到。 1)Action委托 action表示無返回值的泛型委托。 如下代碼:
1 /// <summary> 2 /// Action泛型委托 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 /// <param name="action"></param> 6 public static void TestAction<T>(Action<T> action, T input) 7 { 8 action(input); 9 }
2)Func委托
func表示有返回值的泛型委托。 如下代碼:1 //Func委托 2 var result = list.Where(p => p < 10).ToList();
3)Predicate委托
predicate表示返回bool值的泛型委托。 如下代碼:1 //Predicate委托 2 var result2 = list.Find(p => p == 100);
完整代碼:
1 namespace ConsoleApplication16 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //Linq與泛型委托 8 var list = Enumerable.Range(0, 1000).ToList(); 9 10 //Action委托 11 TestAction<int>(p => Console.WriteLine(p), 10); 12 13 //Func委托 14 var result = list.Where(p => p < 10).ToList(); 15 16 //Predicate委托 17 var result2 = list.Find(p => p == 100); 18 19 Console.ReadKey(); 20 } 21 22 /// <summary> 23 /// Action泛型委托 24 /// </summary> 25 /// <typeparam name="T"></typeparam> 26 /// <param name="action"></param> 27 public static void TestAction<T>(Action<T> action, T input) 28 { 29 action(input); 30 } 31 32 } 33 }