第6章 泛型

1. 概述html

  • 泛型能夠建立獨立於被包含類型的類和方法。
  • 泛型不只限於類,還可用於接口和方法。
  • 泛型優勢:
    • 性能:List<T>類使用時定義類型,故再也不進行裝箱和拆箱操做,即性能高。
    • 類型安全:使用時定義了類型,所以能夠經過編譯檢測出不符合的類型。
    • 二進制代碼重用:定義一次,但能夠用許多不一樣的類型實例化。
    • 代碼的擴展
    • 命名約定:
      • 泛型類型的名稱用字母T做爲前綴
      • 沒有特殊要求,且只使用一個泛型類型,就能夠用字符T做爲泛型類型的名稱
      • 若有特殊要求(如實現接口或派生自基類),或使用兩個或以上泛型類型,就使用描述性的名稱

                               public delegate void EventHandler<TEventArgs>(objcet sender, TEventArgs e);數據庫

                               public delegate TOutput Converter<TInput, TOutput>(TInput from);安全

                               public class SortedList<TKey, TValue> {}ide

2. 泛型類的功能函數

(1) 默認值:default關鍵字性能

  • 不能把null賦予泛型類型 => 緣由是泛型類型既能夠實例化爲引用類型,又能夠實例化爲值類型。而null只能用於引用類型。
  • default關鍵字,將null賦予引用類型,將0賦予值類型。
public T GetDocument()
{
    T doc = defalut(T);
    lock(this)
    {
          doc = documentQueue.Dequeue();
    }
    return doc;
}    

 (2) 約束:this

  • 泛型類須要調用泛型類型中的方法,必須添加約束。
  • 泛型支持的約束類型

  • 能夠合併多個約束,如where T : IFoo,new() 即類型T必須實現IFoo接口,且必須有一個默認構造函數
  • 給泛型類型添加約束時,最好包含泛型參數名稱的一些信息
// TDocument 來代替 T
// 對於編譯器而言,參數名不重要,但更具備可讀性
// 即TDocument類型必須實現IDocument接口
public class DocumentManager<TDocument>
      where TDocument : IDocument
{
     //...
}

 (3) 繼承spa

  • 泛型類型能夠實現泛型接口(IEnumerable<T>),也能夠派生自一個類
  • 派生類能夠是泛型類或非泛型類
public class Base<T>
{
}

// 派生自泛型基類
public class Derived<T> : Base<T>
{
}
// 派生自指定基類的類型
public class Derived<T> : Base<string>
{
}

 

  • 還能夠建立一個部分的特殊操做
public class Query<TRequest, TResult>
{
}
public StringQuery<TRequest> : Query<TRequest, TResult>
{
}

 

(4) 靜態成員:泛型類的靜態成員只能在類的一個實例中共享。翻譯

// StaticDemo<T>類包含靜態字段X
public classs StaticDemo<T>
{
    public static int x;
}
// 同時對不一樣類型使用泛型類
StaticDemo<string>.x = 4;
StaticDemo<int>.x =5;
WriteLine(StaticDemo<string>.x); // writes 4

 3. 泛型接口3d

(1) 協變與抗變:指對參數和返回值的類型進行轉換

  • 協變:參數類型的轉換。能夠理解成:父類 -> 子類。父類的對象用子類替換,也能夠理解成子類當父類用。例如,函數Act的輸入參數爲object類型,實際操做中咱們能夠將string類型的對象傳給函數。
private void button1_Click(object sender, EventArgs e)
{
    string str = "這是一個string類型的實例, 函數Act的參數爲object, 這裏有協變的應用";
    Act(str);
}

void Act(object obj)
{
    return ;
}
  •  抗變:方法的返回類型的轉換。能夠理解成:子類 -> 父類。子類的對象用父類替換,也能夠理解成父類當子類用。抗變也經常翻譯爲逆變。例如,函數Func的返回類型爲string,咱們能夠將返回的值賦給object對象。
private void button2_Click(object sender, EventArgs e)
{
    //注意這裏:Func的返回類型爲string, obj的類型爲object, string類型繼承自object
    object obj = Func();
}

string Func()
{
    return "這裏有抗變的應用";
}

 

(2) 泛型接口的協變:用out關鍵字標註,泛型接口就是協變的。即在接口實現代碼裏面,T只能用做返回類型,不能用做參數類型。

(3) 泛型接口的抗變:用in關鍵字標註,泛型接口就是抗變的。即在接口實現代碼裏面,T用做方法的輸入。

//泛型接口支持協變、逆變和不支持協變、逆變的對比
//1-定義一個接口IFoo,既不支持協變,也不支持逆變。
    interface IFoo<T>
    {
        void Method1(T param);
        T Method2();
    }
    //實現接口IFoo
    public class FooClass<T> : IFoo<T>
    {
        public void Method1(T param)
        {
            Console.WriteLine(default(T));
        }
        public T Method2()
        {
            return default(T);
        }
    }

//2-定義一個接口IBar支持對參數T的協變
    interface IBar<out T>
    {
        T Method();
    }
    //實現接口IBar
    public class BarClass<T> : IBar<T>
    {
        public T Method()
        {
            return default(T);
        }
    }

//3-定義一個接口IBaz支持對參數T的逆變
    interface IBaz<in T>
    {
        void Method(T param);
    }
    //實現接口IBaz
    public class BazClass<T> : IBaz<T>
    {
        public void Method(T param)
        {
            Console.WriteLine(param.ToString());
        }
    }
//---------------應用---------------
//1-定義兩個有繼承關係的類型,IParent和SubClass
    interface IParent
    {
        void DoSomething();
    }
    public class SubClass : IParent
    {
        public void DoSomething()
        {
            Console.WriteLine("SubMethod");
        }
    }

//2-按照協變的邏輯,分別來使用IFoo和IBar
    //IFoo 不支持對參數T的協變
    IFoo<SubClass> foo_sub = new FooClass<SubClass>();
    IFoo<IParent> foo_parent = foo_sub;//編譯錯誤

    //IBar 支持對參數T的協變
    IBar<SubClass> bar_sub = new BarClass<SubClass>();
    IBar<IParent> bar_parent = bar_sub;

//3-按照逆變的邏輯,分別來使用IFoo和IBaz。
    //IFoo 對參數T逆變不相容
    IFoo<IParent> foo_parent = null;
    IFoo<SubClass> foo_sub = foo_parent;//編譯錯誤

    //IBaz 對參數T逆變相容
    IBaz<IParent> baz_parent = null;
    IBaz<SubClass> baz_sub = baz_parent;
泛型接口支持協變、逆變和不支持協變、逆變的對比

部分說明轉自:https://www.cnblogs.com/icyJ/archive/2012/11/16/covariant.html

4. 泛型結構:很是類型於泛型類,知識沒有繼承特性。

  • .NET Framework的一個泛型結構是Nullable<T>
    • 主要用於將數據庫中、XML數據中的能夠爲空的數字與.NET數字(不能爲空)類型進行映射
    • 結構Nullable<T>定義了一個約束:T必須是一個結構
    • 定義了只讀屬性HasValue和Value,以及一些運算符重載。
    • Nullable<T> ==> T 的運算符重載是顯示定義
    • T ==> Nullable<T> 的運算符重載是隱式的
Nullable<int> x;
x = 4;
x += 3;
if (x.HasValue)
{
    int y = x.Value;
}
x = null;
  • 使用「?」運算符,定義可控類型的變量(int? x;)
    • 可空類型能夠與null比較
    • 可空類型能夠與算術運算符一塊兒使用。若兩個可空變量運算,任何一個值爲null,則運算結果爲null。
    • 可空類型轉換爲非可空類型需進行顯式轉換,且使用合併運算符(??)定義一個默認值,與空值映射。
int? x1 = GetNullableType(); //GetNullableType()方法只是一個佔位符,返回一個可空的int
int y1 = x1 ?? 0;            //x1=null時賦值是默認值0

 5. 泛型方法

  • 在泛型方法中,泛型類型用方法聲明來定義
  • 泛型方法能夠在非泛型類中定義
  • 所調用的泛型方法是在編譯期間而非運行期間定義的,所以泛型方法能夠像非泛型方法那樣調用
相關文章
相關標籤/搜索