1. 程式人生 > >詳解C#泛型(一)

詳解C#泛型(一)

安全 情況 重用 模板 信息 普通 cast 綁定 封閉式

  一、C#中的泛型引入了類型參數的概念,類似於C++中的模板,類型參數可以使類型或方法中的一個或多個類型的指定推遲到實例化或調用時,使用泛型可以更大程度的重用代碼、保護類型安全性並提高性能;可以創建自定義的泛型類型(類、結構、接口、委托)和泛型方法;

  1.在泛型類型的定義或泛型方法的聲明中,類型參數是類型的占位符,這些占位符指代的類型需要在實例化泛型類型或調用泛型方法時進行指定;

  ※類型參數一般以T命名,如果是多個,使用T、U、V等,如果有指定約束,可以結合約束命名,例如需要繼承自MyClass的類型參數命名為TMyClass;任何命名都並不會給類型參數增加額外作用;

  2.泛型是運行時起作用的一套機制,根據運行時類型參數被指定為值類型還是引用類型其使用方式有所不同:

  ※當類型參數被指定為值類型時,會在第一次指定該特定值類型的類型時創建該類型唯一的專用化泛型類型,泛型類型中的類型參數會被替換為相應的值類型;

  ※當類型參數被指定為引用類型時,會在第一次指定任意引用類型時創建一個通用化泛型類型,泛型類型中的類型參數會被替換為該引用類型,並在之後每次指定為引用類型時重用該泛型類型並修改其中類型參數的類型;造成這種差異的原因可能在於所有的引用大小相同;

  二、這篇我們先了解下泛型類,泛型類的定義中可以將類型參數用作成員變量的類型或方法中參數列表、返回值的類型:

class MyClass<T> //聲明具有一個類型參數的泛型類,可以有多個類型參數,用,隔開:<T, U>
{ public T MyObj; //聲明T類型的字段 private Type myGenericField = typeof(T); //在泛型類內部可以獲取類型參數的類型信息 //do… } //聲明泛型類的實例,指定類型參數為int類型 MyClass<int> myObj = new MyClass<int>();

  1.在定義泛型類型時,可以對類型參數的種類添加限制,這些限制稱為約束(Constraint),使用約束可以增加類型參數所能進行操作和調用方法的數量;約束使用上下文關鍵字where指定,位於基類和接口之後,例如:

class MyClass<T> : MyBaseClass where
T : MyType //指定基類約束,T需要是指定的類MyType或繼承自類MyType,基類約束需要在所有約束之前,基類約束本身也可以是泛型類型,例如MyType<T>

  ※其它特殊的約束:

  where T : IMyInterface //指定接口約束,T需要是指定的接口IMyInterface或實現接口IMyInterface,可以同時指定多個接口約束,接口約束本身也可以是泛型類型,例如IMyInterface<T>
  where T : class //指定類型約束,T需要是類類型
  where T : struct //指定類型約束,T需要是值類型,但不可以是可空類型
  where T : new() //類型參數必須有公共的無參數構造函數,與其他約束一起使用時,new()約束必須最後指定;由於結構的定義中一定包含無參數構造函數,所以struct約束包含new()約束,二者不可同時使用,通常與class約束一起使用:class, new()
  where T : struct where U : class //給多個類型參數指定約束
  where U : T //類型參數作為約束,類型參數U繼承自類型參數T

  ※可以對一個類型參數應用多個約束,多個約束使用,隔開:where T : MyType, IMyInterface;

  ※沒有約束的類型參數稱為未綁定類型參數(Unbounded Type Parameter),這些類型參數的變量在使用時不可以使用==和!=運算符,因為無法保證運行時指定的類型支持這些運算符;

  ※對於使用類型約束class的類型參數,應避免對其變量使用==和!=運算符,因為在泛型中這些運算符僅會根據引用來判斷是否相等,即使類型對==和!=運算符進行了重載也不行;如果必須根據值進行判斷,應給類型參數加入基類約束IEquatable<T>或IComparable<T>並在類型中實現它們,比較時使用Equals或CompareTo;

  ※從C#7.3開始,可以使用特殊類型System.Delegate、System.MulticastDelegate和System.Enum作為基類約束中的基類,還可以使用非托管類型unmanaged作為類型約束中的類型;

  2.非泛型類(即具體類,Concrete Class)只可以繼承自具體類或封閉式構造類(即指定了所有類型參數的泛型類,Closed Constructed Class),不可以繼承自開放式構造類(即沒有完全指定所有類型參數的泛型類,Open Constructed Class);泛型類可以繼承自具體類和封閉式構造類,也可以繼承自開放式構造類,繼承自開放式構造類時,派生類的類型參數中必須包含基類中未指定類型的類型參數,同時,派生類中這些類型參數的約束必須為基類中對應類型參數約束的超集;可以總結為以下幾種情況:

//對於基類為具體類或僅有一個類型參數的情況
class BaseClass { }
class BaseGenericClass<T> { }
//定義一個泛型類,繼承具體類BaseClass
class MyClass<T> : BaseClass { }
//定義一個泛型類,繼承自指封閉式構造類BaseGenericClass<int>
class MyClass<T> : BaseGenericClass<int> { }
//定義一個泛型類,繼承自開放式構造類BaseGenericClass<T> 
class MyClass<T> : BaseGenericClass<T> { }
//定義一個非泛型類,繼承自封閉式構造類BaseGenericClass<int>
class MyClass : BaseGenericClass<int> { }
//對於基類有多個類型參數的情況
class MultipleBaseGenericClass<T, U> { }
//定義一個泛型類,繼承自開放式構造類MultipleBaseGenericClass<T, int>
class MyClass<T> : MultipleBaseGenericClass<T, int> { }
//定義一個泛型類,繼承自開放式構造類MultipleBaseGenericClass<T, U>
class MyClass<T, U> : MultipleBaseGenericClass<T, U> { }
//定義一個非泛型類,繼承自封閉式構造類MultipleBaseGenericClass<int, string>
class MyClass : MultipleBaseGenericClass<int, string> { }

  ※在繼承中,派生類類型的對象可以通過隱式轉換賦值給基類類型的變量,這同樣適用於泛型類型的對象,例如泛型類List<T>繼承自泛型接口IList<T>,那麽可以把List<int>類型的對象隱式轉換為IList<int>類型的變量:

IList<int> iList = new List<int>();

  3.泛型類為不可變量,泛型類型相同但類型參數指定類型不同的泛型類型即是不同的類型,它們之間不能進行類型轉換(即使類型參數指定的類型之間存在類型轉換關系),例如不能把List<DerivedClass>類型的對象賦值給一個List<BaseClass>類型的變量;

  4.泛型類最常見的用途是創建泛型集合類,在命名空間System.Collections.Generic中包含系統定義的各種泛型集合類,應盡可能的使用這些泛型集合來代替普通的集合;


如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的認可是我寫作的最大動力!

作者:Minotauros
出處:https://www.cnblogs.com/minotauros/

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

詳解C#泛型(一)