探討c#中的泛型、委託、泛型委託、Action和Func及使用場景系列之一:泛型
本系列將分別介紹泛型和委託的概念,比較泛型和委託在使用場景上的本質不同點,
最後介紹泛型委託結合起來如何使用及.net自身提供的兩個泛型委託Action和Func。
-------------------------------------------------------------------分隔符----------------------------------------------------------------------------
泛型是C#2.0中推出的功能,目的是解決型別強制轉換的效率和風險問題,泛型可以作用於介面、類、方法、事件和委託,
在使用泛型前,我們先看泛型是如何引入的,考慮下面的開發場景:向一個集合中新增元素,然後遍歷輸出元素,程式碼如下:
1 public class Class1 2 { 3 public void Test() 4 { 5 ArrayList list = new ArrayList(); 6 list.Add("aaa"); 7 list.Add("bbb"); 8 9 for(int i=0;i<list.Count;i++) 10 { 11 string str = (string)list[i]; 12 } 13 } 14 15 }
因為ArrayList中的元素是object 型別的,所以用Add( )方法新增元素的時候有一個裝箱(Boxing)的過程,
這樣在遍歷使用的時候需要做拆箱(UnBoxing)的操作,即強制將object轉化成string型別後再使用。
在裝箱和拆箱的過程中就會損失效能和效率,而且弱型別對程式設計不友好,會帶來意外的風險,換成泛型集合後代碼如下:
1 public void Test2() 2 { 3 List<string> list2 = new List<string>(); 4 list2.Add("aaa"); 5 list2.Add("bbb"); 6 7 for (int i = 0; i < list2.Count; i++) 8 { 9 string str = list2[i]; 10 } 11 }
宣告集合類的時候就指明該集合中的元素只能接收字串List<string>( ) ,遍歷的時候也無需強制轉換。
場景二 : 根據傳入的值比較大小並返回較大的那個值
1 public class Class1 2 { 3 private int x1, y1; 4 public Class1(int x, int y) 5 { 6 this.x1 = x; 7 this.y1 = y; 8 } 9 public int GetBigger() 10 { 11 return x1 > y1 ? x1 : y1; 12 } 13 14 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 Class1 c1 = new Class1(3, 5); 7 int i = c1.GetBigger(); 8 Console.WriteLine(i); 9 10 11 Console.ReadLine(); 12 } 13 }
如果我們要比較兩個浮點數的大小,Class1是不能複用的,必須再寫Class2來滿足這樣的需求,
可以預見,Class1和Class2除了型別不同,程式碼幾乎一模一樣,
這裡該泛型類上場了,泛型特性提供了一種更優雅的方式,讓多個型別共享一組程式碼,
我們用泛型來改造Class1,將Class2寫成一個泛型類,如下:
1 public class Class2<T> where T: IComparable 2 { 3 private T x1, y1; 4 public Class2(T x, T y) 5 { 6 this.x1 = x; 7 this.y1 = y; 8 } 9 public T GetBigger() 10 { 11 return x1.CompareTo(y1)>0 ? x1 : y1; 12 } 13 }
在Class2中,我們在類名Class2後加了“<T>”這樣一個泛型標記,T是一個型別佔位符,用尖括號括起來放在類名後面,
有了這個聲明後在這個類的內部就可以使用這個型別," where T: IComparable " 為泛型約束,它告訴外部的呼叫者,
傳入的型別必須實現IComparable這個介面,否則編譯器就會報錯。因為型別 T 實現了IComparable介面,
所以GetBigger()方法中可以呼叫型別的CompareTo()方法來比較兩個數的大小(c#中所有的數值型別都實現了IComparable介面)。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 Class1 c1 = new Class1(3, 5); 7 int i = c1.GetBigger(); 8 Console.WriteLine(i); 9 10 11 Class2<double> c2 = new Class2<double>(3.5, 5.2); 12 double i2 = c2.GetBigger(); 13 Console.WriteLine(i2); 14 15 16 Console.ReadLine(); 17 } 18 }
在呼叫的時候,可以明顯看到Class1和Class2構造的不同,Class2後面多了" <double> " 這樣一個泛型宣告,
所以Class2建構函式傳入的數必須是double型的,如果我們要比較整型數的大小,就可以直接用Class2這個泛型類了,
程式碼如下(紅色部分) :
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 Class1 c1 = new Class1(3,5); 7 int i = c1.GetBigger(); 8 Console.WriteLine(i); 9 10 11 Class2<double> c2 = new Class2<double>(3.5, 5.2); 12 double i2 = c2.GetBigger(); 13 Console.WriteLine(i2); 14 15 16 Class2<int> c22 = new Class2<int>(99, 87); 17 int i22 = c22.GetBigger(); 18 Console.WriteLine(i22); 19 20 21 Console.ReadLine(); 22 } 23 }
只需在宣告的時候傳入int 型別就可以了,這樣程式碼寫起來就舒服多了。
--------------------------------------------------------------------分隔符---------------------------------------------------------------------------------
接下來我們看泛型方法的使用 :
明白了泛型類的使用,泛型方法的使用是類似的,定義泛型方法只需要在方法名後加上 "<T>"就可以了,如果有兩個泛型型別,
就用逗號分隔兩個泛型識別符號,如 "<T1, T2>",多個泛型型別以此類推,我們將上面比較大小的功能用泛型方法來實現,程式碼如下:
1 public class Class3 2 { 3 public T GetBigger<T>(T x1, T y1) where T:IComparable 4 { 5 return x1.CompareTo(y1) > 0 ? x1 : y1; 6 } 7 }
方法名GetBigger和方法引數之間加上泛型標記 "<T>" ,因為是比較兩個數的大小,所以要對型別T用where做一下約束,
型別T必須實現IComparable介面(這個介面只有一個CompareTo( )方法), 這樣型別變數 x1 才能呼叫CompareTo( ) 方法來比較大小。
呼叫的方式如下(見紅色程式碼部分):
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 Class1 c1 = new Class1(3,5); 7 int i = c1.GetBigger(); 8 Console.WriteLine(i); 9 10 11 Class2<double> c2 = new Class2<double>(3.5, 5.2); 12 double i2 = c2.GetBigger(); 13 Console.WriteLine(i2); 14 15 16 Class2<int> c22 = new Class2<int>(99, 87); 17 int i22 = c22.GetBigger(); 18 Console.WriteLine(i22); 19 20 21 Class3 c3 = new Class3(); 22 int i3 = c3.GetBigger<int>(82, 37); 23 Console.WriteLine(i3); 24 25 26 Console.ReadLine(); 27 } 28 }
接下來一篇將介紹c#中的委託。