URLhttps://learnscript.net/zh-hant/programming/object-oriented/generics/
    複製連結移至說明  範例

    泛型,型別參數,泛型條件約束介紹

    閱讀 5:38·字數 1691·發佈 
    Youtube 頻道
    訂閱 375

    泛型

    泛型可用於推遲資料型別的確定,這項工作在正常的定義過程中往往需要立即進行。比如,在定義類別時,你需要指定欄位的資料型別,方法參數的資料型別等。

    為何使用泛型?

    很明顯,泛型減少了設計層面的資料型別轉換,同時保持了程式碼的適用性和簡便性。

    型別參數

    泛型使用型別參數來代替本應被確定的資料型別,雖然也被稱為參數,但型別參數的作用類似於預留位置。在對使用了泛型的目標進行具現化或呼叫時,需要給出型別參數,比如 C# 中常見的泛型類別List<T>,將其型別參數T取代為某種具體的資料型別後,具現化才能進行。

    下面的 C# 程式碼具現化了泛型類別List<T>

    *.cs
    // 型別參數 T 被指定為 string
    List<string> nicknames = new();

    泛型方法

    如果你希望定義一個泛型方法,那麽該方法需要擁有自己的型別參數,他不能與類別或介面的型別參數相同,如果類別或介面也運用了泛型的話。

    下面的 C# 類別Box擁有一個泛型方法GetFirst,用於尋找第一個資料型別與T一致的元素。

    generics.cs
    // 類別 Box,表示儲存內容的箱子
    class Box
    {
    	// 欄位 items,一個簡單的陣列
    	object[] items;
    
    // 建構子,可以初始化 items public Box(params object[] i) { items = i ?? Array.Empty<object>(); }
    // 泛型方法 GetFirst,取得陣列中第一個型別為 T 的元素 public T? GetFirst<T>() { // 如果元素的型別與 T 一致,則傳回 foreach (object item in items) if (item.GetType() == typeof(T)) return (T)item;
    return default; } }

    使用泛型方法GetFirst尋找第一個字串,整數,浮點數型別的元素。

    *.cs
    // 建立 Box 的執行個體 box
    Box box = new("第一個字串", 123, "第二個字串", 234, 0.123f, 0.234f);
    // 取得 box 中的第一個字串
    Console.WriteLine(box.GetFirst<string>());
    // 取得 box 中的第一個整數
    Console.WriteLine(box.GetFirst<int>());
    // 取得 box 中的第一個浮點數
    Console.WriteLine(box.GetFirst<float>());
    第一個字串
    123
    0.123

    泛型條件約束

    泛型條件約束主要用於限製泛型的使用,比如,限製型別參數可以指定的資料型別。對型別參數加以約束是理所當然的,因為他給予了開發人員很大的選擇範圍,沒人願意型別參數被指定為一種無法被處理的資料型別。

    我們為GetFirst方法的型別參數T增加約束,使其只能被指定為參考型別,之前書寫的box.GetFirst<int>()box.GetFirst<float>()將產生錯誤。

    generics.cs
    public T? GetFirst<T>() where T : class
    {
    	// …
    }

    泛型的處理

    對於接下來講述的內容,我們作出如下假設,語言包含泛型類別List<T>,以及參考型別object,實值型別intfloat

    在程式碼編譯期間,泛型可能會以異構或同構的方式被處理,這取決於具體的語言編譯器。

    異構會為不同的型別參數組建不同的程式碼,如果你使用了List<int>List<float>,那麽編譯器需要為他們產生兩個不同的類別,以將型別參數T分別取代為intfloat

    同構會將泛型的型別參數轉換為最寬泛的資料型別,比如,無論你書寫List<int>還是List<float>,編譯器都會將型別參數T取代為object,因此,相對於異構,同構產生的類別只有一個而不是多個。在這種情況下,編譯器可能還需要調整某些程式碼,以完成額外的型別轉換,畢竟,在開發人員書寫的程式碼中,List<int>List<float>可以直接關聯intfloat型別的值,這些值的型別與編譯階段出現的object型別存在轉換的必要。

    為何采用異構的泛型能夠改善效率?

    假如資料型別的轉換將消耗大量資源,比如,參考型別與實值型別之間的相互轉換,那麽采用異構的泛型會是一種很好的紓困方案,因為他使得相關的轉換不再存在。

    上述中的資料型別轉換之所以會發生,是由於設計者希望采用一種寬泛的標準來涵蓋所有的可能,比如,使用陣列object[]來儲存所有資料型別的值,當你將一個整數型別的值存入陣列,或將其從陣列中取出以進行算術運算時,參考型別object與實值型別int之間的轉換就會發生。而這樣的情況會在使用異構泛型後消失,因為最終建置的陣列是int[]而非object[],將int型別的值存入int[]當然不需要任何轉換。

    堆疊,堆積

    要想深入了解實值型別和參考型別,你可以檢視堆積和參考一段。

    原始碼

    generics.cs·codebeatme/programming·GitHub