一.為什么要提出泛型的概念
我們在聲明對象或者方法中,對象中成員變量的定義或者函數參數都傳遞都要指定具體的對象類型,但是有的時候參數的類型是變化的,但是實現的功能卻又差不多,這個時候我們就想,是否存在一種東西可以將參數的位置“占住”,當傳遞具體的對象類型是再用這個類型取替換被占住的位置,這個時候就提出了泛型的概念,是不是有點繞,但是看完下面的例子就清除這里表達的內容了,泛型有多種表現形式,泛型類,泛型方法,泛型集合,泛型委托,可以說不懂泛型就沒有真正的了解C#,下面讓我們來開始泛型的學習吧。
二.泛型類,泛型方法
我們先舉個例子,我們定義一個類來模擬入棧出棧操作,我們操作出棧入棧時要針對各種數據類型,int型,double 型,字符型......總之各種類型都有可能,我們不可能針對每個類型都寫一個類來操作出棧入棧,這顯然是不現實的,這個是時候就該泛型大顯身手發時候了,看下面的定義:
public class MyStack<T> //多種類型時可以用T1,T2,T3......來表示 { public T[] objStack; public int _stackPoint; public int _stackSize; public MyStack(int Size) //成員變量一般在初始化的時候都要賦值 { objStack = new T[Size]; _stackSize = Size; _stackPoint = -1; } /// <summary> /// 入棧操作 /// </summary> /// <param name="item"></param> public void Push(T item) //這里要把T當成一種數據類型 { if (_stackPoint > _stackSize) { return; } else { _stackPoint++; objStack[_stackPoint] = item; } } /// <summary> /// 出棧操作 /// </summary> /// <returns></returns> public T Pop() { if (_stackPoint > 0) { _stackPoint--; return objStack[_stackPoint]; } else { return objStack[0]; } } }
我們在 public class MyStack<T> 后面加了一個<T>這個時候這個類就變成了一個泛型類,表示一個占位符,當我們實例化該類的時候需要傳入具體的數據類型,我們來看一下泛型類的具體用法:
public int[] arrayInt = new int[6]; public string[] arrayStr = new string[6]; MyStack<int> objMyIntStack = new MyStack<int>(6); MyStack<string> objMyStrStack = new MyStack<string>(6);
這樣泛型類就可以操作int 類型 和 string類型進行出棧入棧操作但代碼卻不需要改動。
三.泛型集合
使用泛型集合首先是是加了類型安全,方便編程,泛型集合指定了類型后只能將同類型的參數放入集合,泛型集合最常用就是List集合和Dictionary集合,我們分別看一下這兩種集合。
A.Lis<T> 泛型集合
說到List泛型集合就不得不說ArrayList集合,ArrayList集合在操作是需要進行強制類型,極大的降低了代碼處理效率所以,List集合應運而生,讓我們看如下代碼做個比較:
//用ArrayList集合來存儲 ArrayList objArrayList = new ArrayList(); Students stu1 = new Students() { Name="小紅",Age=20}; Students stu2 = new Students() { Name = "小明", Age = 30 }; objArrayList.Add(stu1); objArrayList.Add(stu2); Students obj = (Students)objArrayList[0]; Console.WriteLine(obj.Name + obj.Age.ToString()); //這里需要進行強制類型轉換 Console.ReadLine(); //用List集合來存儲 List<Students> objList = new List<Students>() { new Students(){Name = "小紅",Age=20}, new Students(){Name="小明",Age = 30} }; foreach (var item in objList) { Console.WriteLine(item.Name + "," + item.Age.ToString()); } Console.ReadLine();
除此之外,我們一直在講泛型集合可以保證數據安全,和ArrayList相比它的數據到底安全在什么地方呢,我們通過下面的例子做進一步說明:
ArrayList objArrayList = new ArrayList(); Students stu1 = new Students() { Name="小紅",Age=20}; Students stu2 = new Students() { Name = "小明", Age = 30 }; Teacher tea1 = new Teacher() { Name = "小剛", Age = 30 }; objArrayList.Add(stu1); objArrayList.Add(stu2); objArrayList.Add(tea1); //Teacher類也可以添加進來,類型不安全 foreach (var item in objArrayList) { Students obj00 = (Students)item; }
從例子可以看出ArrayList集合的Add方法參數是object類型,所以Teacher的數據類型也可以放進去,這顯然不是我們想要的,但是泛型集合就不一樣,當占位符被確定的數據類型占用后,別的數據類型就添加不到集合中去。
List集合的常用方法,List集合中有很多方法,我們重點將一下Sort方法,Sort方法有四個重載方法,public void Sort();,public void Sort(Comparison<T> comparison);,
public void Sort(IComparer<T> comparer);,public void Sort(int index, int count, IComparer<T> comparer);我們直接調用Sort方法是按默認升序排序,假如某個類實現了IComparable接口那么默認排序就是按照接口中定義的方法來排序,看下面的例子:
//List集合排序 List<int> intList = new List<int>() { 1, 4, 3, 11, 8, 2, 0 }; intList.Sort(); foreach (var item in intList) { Console.WriteLine(item.ToString()); } Console.ReadLine();
輸出結果為:0,1,2,3......結果為升序排序
//字符串排序 List<string> objListStr = new List<string>() { "c","a","b"}; objListStr.Sort(); foreach (var item in objListStr) { Console.WriteLine(item); } Console.ReadLine();
輸出結果為:a,b,c
假如是對象類型呢,默認的排序方法為升序排序,但是對象之間沒有升序的概念,這個時候該怎么辦呢,看下面的代碼:
public class Students : IComparable<Students> { public string Name { get; set; } public int Age { get; set; } /// <summary> /// 實現泛型接口 /// </summary> /// <param name="other"></param> /// <returns></returns> public int CompareTo(Students other) { return other.Name.CompareTo(this.Name); } } static void Main(string[] args) { Students stu1 = new Students() { Name = "Mick", Age = 20 }; Students stu2 = new Students() { Name = "Jack", Age = 30 }; List<Students> objList = new List<Students>(); objList.Add(stu1); objList.Add(stu2); objList.Sort(); foreach (var item in objList) { Console.WriteLine(item.Name); } Console.ReadLine(); }
Students類中實現了泛型接口IComparable<T> ,在泛型接口的方法中我們可以寫排序的方式,這樣做確實可以解決對象排序的問題,但是假如我們的排序條件是變化的,這種方式顯然又不能滿足我們的需求了,讓我i們接着往下探索,如何實現集合對象的動態排序,讓我們看如下代碼:
/// <summary> /// 按姓名降序排列 /// </summary> public class NameDesc:IComparer<Students> { public int Compare(Students x, Students y) { return y.Name.CompareTo(x.Name); } } /// <summary> /// 按姓名升序排序 /// </summary> public class NameAsc : IComparer<Students> { public int Compare(Students x, Students y) { return x.Name.CompareTo(y.Name); } } /// <summary> /// 按年齡降序 /// </summary> public class AgeDesc:IComparer<Students> { public int Compare(Students x, Students y) { return y.Age - x.Age; } } /// <summary> /// 按年齡升序 /// </summary> public class AgeAsc : IComparer<Students> { public int Compare(Students x, Students y) { return x.Age.CompareTo(y.Age); } }
我們定義了一個自定義排序類,自定義排序類實現了ICompare接口。
static void Main(string[] args) { Students stu1 = new Students() { Name = "Mick", Age = 20 }; Students stu2 = new Students() { Name = "Jack", Age = 30 }; List<Students> objList = new List<Students>(); objList.Add(stu1); objList.Add(stu2); objList.Sort(new AgeDesc()); //基於接口實現多態的典型應用 foreach (var item in objList) { Console.WriteLine(item.Name); } Console.ReadLine(); }
調用List.Sort的重載方法,這里基於接口實現了多態,需要好好體會,關於集合的排序我們還可以用Linq查詢。
B.Drictionary<> 泛型集合
List集合用索引查找元素的方法顯然沒有辦法滿足我們的實際需求,為了彌補這個缺陷,我們引入了字典的概念,說到鍵值對查詢又不得不說說Hashtable,早期鍵值對集合都是用Hashtable類來實現的,后來泛型集合出現后Dictionary泛型集合取代了Hashtable類,讓我們來看看兩者的區別:
//用Hashtable集合 Hashtable objHashtable = new Hashtable(); objHashtable.Add("student1", new Students() { Name = "小王", Age = 20 }); objHashtable.Add("student2", new Students() { Name = "小李", Age = 25 }); Students stu =(Students)objHashtable["student1"]; //需要進行強制類型轉換 //用Dictionary集合 Dictionary<string, Students> objDictionary = new Dictionary<string, Students>(); objDictionary.Add("student1", new Students() { Name = "小王", Age = 20 }); objDictionary.Add("student2", new Students() { Name="小李",Age = 25}); Students myStudent = objDictionary["student1"]; //不需要進行強制類型轉換
從例子可以看出Hashtable集合操作都是object的類型,在進行對象操作是需要進行強制類型轉換,但是Dictionary卻不一樣,不需要進行強制類型轉換,所以可以這樣講Dictionary出現以后可以完全替Hashtable。
四.泛型委托
A.自定義泛型委托
static void Main(string[] args) { Mydelegate<int> objMydelegate = Add; Console.WriteLine("結果為:{0}", objMydelegate(1, 2)); Console.ReadLine(); } static int Add(int i1,int i2) { return i1 + i2; } } public delegate T Mydelegate<T>(T t1, T t2); //自定義泛型委托
以上例子就簡單展示了自定泛型委托的使用方法,但是每次都這這么定義委托似乎很不方便,所以微軟的工程師預先給我們定義好了幾個泛型委托,我們可以直接使用,大大提高了使用泛型委托的便捷程度。
B.Func泛型委托的使用
Func是一個帶返回值的泛型委托,func有多個重載版本,需要注意的是func最后一個參數是返回值類型,如果前面有泛型類型的參數,這個參數就是委托方法的形參類型,簡單說func泛型委托就是一個帶返回值的方法簽名,我們先來看看它的簡單應用:
static void Main(string[] args) { Func<int, int, int> objFunc = (a, b) => { return a + b; }; Console.WriteLine("結果為:{0}", objFunc(2, 5)); Console.ReadLine(); }
有人會說這樣用似乎沒什么意義,我們調用方法就可以直接實現功能,干嘛還要從委托轉一下似乎多此一舉,但是事實並不是如此,讓我們看一下Func的復雜用法。現在提出一個需求,要求計算數組中任意指定開始位和結束位的“算數和” and 算數積。常規做法是:
static int GetSum(int[] nums, int from, int to) { int result = 0; for (int i = from; i <= to; i++) { result += nums[i]; } return result; } static int GetMulti(int[] nums, int from, int to) { int result = 1; for (int i = from; i <= to; i++) { result *= nums[i]; } return result; }
寫兩個方法,分別計算和與積,但是還有別的實現方法么,答案是肯定的:
static void Main(string[] args) { int[] nums = { 1, 2, 10, 4, 5, 6, 7, 8, 9 }; Console.WriteLine("數組前三個元素的和為:{0}", CommonMethod((a, b) => { return a + b; }, nums, 0, 3)); Console.WriteLine("數組前三個元素的積為:{0}", CommonMethod((a, b) => { return a * b; }, nums, 0, 3)); Console.ReadLine(); } static int CommonMethod(Func<int, int, int> com, int[] nums, int a, int b) { int result = nums[a]; for (int i = a + 1; i < b; i++) { result = com(result, nums[i]); } return result; }
其實這里也體現了委托的本質,委托本來就是為了把方法當成參數傳遞而設計的。
C.Action泛型委托
Action泛型委托和func泛型委托差不多,只不過Action是不帶返回值的方法的簽名,看下面的例子我們就可以了解Action泛型委托的用法:
static void Main(string[] args) { Action<string> objAction = (a) => { Console.WriteLine(a); }; objAction("Hello C#"); Console.ReadLine(); }
D.Predicate泛型委托
Predicate<T>委托定義如下:
public delegate bool Predicate<T>(T obj);
解釋:此委托返回一個bool值的方法
在實際開發中,Predicate<T>委托變量引用一個“判斷條件函數”,
在判斷條件函數內部書寫代碼表明函數參數所引用的對象應該滿足的條件,條件滿足時返回true
看下面的例子:
static void Main(string[] args) { List<int> objList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; List<int> resultList = objList.FindAll((s) => { return s > 2; }); //Predicate委托 foreach (var item in resultList) { Console.WriteLine(item); } Console.ReadLine(); }
好的以上就是關於泛型概念的總結,希望可以幫到有需要的人。