歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 理解C#泛型

理解C#泛型

日期:2017/3/1 9:33:14   编辑:Linux編程

在C# 2.0中引入了泛型,泛型的出現解決了編碼中的很多問題。相信大家一定經常用到"System.Collections.Generic"命名空間中的泛型集合類("Generic"就是泛型的意思)。在C# 1.0中,我們還在使用"System.Collections"命名空間中的非泛型集合類,那麼看看我們在沒有泛型的時候遇到的問題。

問題1:強制類型轉換

ArrayList stuList = new ArrayList();
Student wilber = new Student { Name = "Wilber", Age = 27, Gender = "Male" };
stuList.Add(wilber);
Student stu = (Student)stuList[0];
stuList.Add(10);

在使用非泛型集合ArrayList時,所有的對象都是以object類型加入ArrayList,當對象從ArrayList取出的時候也是object類型,這時我們就需要進行強制類型轉換,如果轉換不當,就會得到一個運行時的錯誤;即使我們向ArrayList添加不同類型的對象時,也不會報錯(例如上面向stuList中加入了一個int值)。

問題2:裝箱和拆箱

在上面的例子中,如果我們使用ArrayList存放一組值類型的數據(例如一組int值),存入時,每個值類型的數據都要進行裝箱為object類型;取出時,每個object類型的數據又要進行拆箱操作。

可以看到,在使用非泛型集合的時候,用戶需要自己進行類型轉換,並且可能遇到運行時的類型轉換異常;同時,對於值類型的操作 ,非泛型集合會有裝箱和拆箱帶來的效率問題。

泛型的出現

對於上面的問題,我們可以使用C# 2.0中的泛型集合。

這樣一來,我們就通過類型參數(例子中的Student)來限制List可以包含的實例類型,從而避免的強制類型轉換。

同時,通過類型參數,編譯器可以進行類型檢查,當試圖往List中存入一個與類型參數不匹配的對象的時候,編譯器就是給出錯誤提示。

List<Student> stuList = new List<Student>();
Student wilber = new Student { Name = "Wilber", Age = 27, Gender = "Male" };
stuList.Add(wilber);
Student stu = stuList[0];
stuList.Add(10);

泛型中的術語

下面我們看看泛型中的一些概念和術語。

泛型有兩種表現形式:泛型類型(包括類、接口、委托和結構,沒有泛型枚舉)和泛型方法。在泛型類型和泛型方法中都會有類型參數,當通過泛型類型實例化對象或者對泛型方法調用的時候,都需要使用一個真實的類型來代替類型參數。

類型參數是真實類型的占位符,在泛型聲明過程中,所有的類型參數放在一對間括號中(<>),通過逗號分隔。

泛型類型

根據類型參數不同的指定類型實參的情況,泛型類型可以分為:

  • 如果沒有為類型參數提供類型實參,那麼聲明的就是一個未綁定泛型類型(unbound generic)
  • 如果指定了類型實參,該類型就稱為已構造類型(constructed type),然而已構造類型又可以是開放類型或封閉類型的
    • 包含類型參數的類型就是開放類型(open type)(所有的未綁定的泛型類型都屬於開放類型的),
    • 每個類型參數都指定了類型實參就是封閉類型(closed type)

類型是對象的藍圖,我們可以通過類型來實例化對象;那麼對於泛型來說,未綁定泛型類型是以構造泛型類型的藍圖,已構造泛型類型又是實際對象的藍圖。

下圖就是一個簡單的例子,Dictionary<TKey, TValue>就是一個泛型類型(未綁定泛型類型,開放類型);通過制定類型參數,可以得到不同的封閉類型;通過不同的封閉類型有可以構造不同的實例。

泛型方法

我們都已經習慣了方法的參數和返回值擁有固定的類型,這裡就看看“參數化”的方法。對於泛型方法,可以理解為擁有類型參數的方法。

對於上面例子中Dictionary<TKey, TValue>這個泛型類型,有很多方法可以使用,例如:

  • void Add(TKey, key, TValue value)
  • bool ContainsValue(TValue value)
  • bool ContainsKey(TKey key)

注意,這些方法中沒有一個是真正的泛型方法,他們只是使用了泛型類型的類型參數

真正的泛型方法應該擁有自己的類型參數,當我們使用泛型方法的時候,要給泛型方法的類新參數指定類型實參,接下來看一個泛型方法的例子。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("The bigger one is {0}", GetBiggerOne<int>(3,9));
        Console.WriteLine("The bigger one is {0}", GetBiggerOne<string>("Hello", "World"));

        Console.Read();
    }

    public static T GetBiggerOne<T>(T itemOne, T itemTwo) where T : IComparable
    {
        if (itemOne.CompareTo(itemTwo) > 0)
        {
            return itemOne;
        }
        return itemTwo;
    }
}

在上面的例子中,我們使用泛型方法來實現一個兩個元素比較的例子,我們看到方法"GetBiggerOne"擁有自己的類型參數,當我們看到一個泛型方法時,可以一步步用真實的類型替換泛型方法中的類型參數,這樣就會簡化我們的分析。

對於泛型的類型約束,將在下面一篇文章介紹。

泛型的優點

根據上面的分析,可以看到泛型有一些的優點:

  • 代碼重用
    • 泛型提供的代碼的重用,確切的說應該是 "邏輯和算法的重用"。從前面的泛型方法例子可以看到,通過泛型可以避免為每種特定的類型實現一個比較方法。
  • 類型安全
    • 泛型類型保證了類型安全,可以在編譯期就發現類型不匹配的問題,而不是等到運行時
  • 效率
    • 避免值類型的裝箱和拆箱引起的效率問題(後面會簡單介紹為什麼泛型可以避免裝箱和拆箱)

總結

泛型的出現,給我們帶來了很多好處,泛型實現了類型和方法的"參數化"。

基於泛型,我們可以實現代碼重用,並且泛型為我們提供了類型安全檢查。對於值類型的操作,通過泛型可以避免裝箱和拆箱帶來的性能損失。

同樣C# 2.0 以後,就建議只在代碼中使用支持泛型的集合類了(System.Collections.Generic)。

Copyright © Linux教程網 All Rights Reserved