天天看點

淺談C#泛型

一.為什麼要提出泛型的概念

我們在聲明對象或者方法中,對象中成員變量的定義或者函數參數都傳遞都要指定具體的對象類型,但是有的時候參數的類型是變化的,但是實作的功能卻又差不多,這個時候我們就想,是否存在一種東西可以将參數的位置“占住”,當傳遞具體的對象類型是再用這個類型取替換被占住的位置,這個時候就提出了泛型的概念,是不是有點繞,但是看完下面的例子就清除這裡表達的内容了,泛型有多種表現形式,泛型類,泛型方法,泛型集合,泛型委托,可以說不懂泛型就沒有真正的了解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();
        }      

好的以上就是關于泛型概念的總結,希望可以幫到有需要的人。