天天看點

C#泛型——使類型參數化

泛型中的靜态字段和靜态函數

  實際類型參數代替泛型參數時,編譯器會根據不同的類型實參重新生成類型,對于編譯器來說,每個重新生成的封閉泛型類型都是一個不一樣的類型,是以它們都有屬于它自己的靜态字段和靜态函數。

為什麼要用泛型

  1. 封裝泛型方法實作代碼的重用
  2. 避免裝箱和拆箱導緻性能的損失

類型參數的限制

 類型限制就是用where關鍵字來限制某個類型實參的類型。

引用類型限制

  引用類型限制的表示形式為T:class,它確定傳遞的類型實參必須是引用類型。限制的類型參數和類型本身沒有關系,即在定義一個泛型結構體時,泛型類型一樣可 以被限制為引用類型。此時,結構體類型本身是值類型,而類型參數限制為引用類型,它可以為 任何的類、接ロ、委托或數組等,但不能指定以下這些特殊的引用類型:System.Object. System.Array 、 System.Delegate、System.MulticastDelegate 、System.ValueType、System.Enum 和System.Void。如下面的代碼所定義的泛型類:

using System.IO;
public class Sample<T> where T : Stream
{
    public void Test(T stream)
    {
        stream.Close();
    }
}
      

  在以上代碼中,類型參數T設定了引用類型限制。whereT:stream的意思就是告訴編譯器:傳入的類型實參必須是System.IO.Stream,或者是從Stream類。如果一個類型參數沒有指定限制,則預設T為System.Object類型。

值類型限制

  值類型限制的表示形式為T:struct,它確定傳遞的類型實參是值類型(包括枚舉),但這裡的值類型不包括可空類型

public class Sample<T> where T : struct
{
    public static T Test()
    {
        return new T();
    }
}
      

  在上面的代碼中,newT()是可以通過編譯的,因為T是一個值類型,而所有值類型都有一個公共的無參構造函數。但如果不對T進行限制,或限制為引用類型,則上面的代碼就會報錯,因 為有的引用類型是沒有公共的無參構造函數的。

構造函數類型限制

  構造函數類型限制的表示形式為T:new(),如果類型參數有多個限制,則此限制必須最後指定。構造函數類型限制確定指定的類型實參有一個公共無參構造函數的非抽象類型。這适用于所有值類型,所有非靜态、非抽象、沒有顯式聲明構造函數的類,以及顯式聲明了一個公共無參構造函數的所有非抽象類。

這裡需要注意,如果同時指定構造器限制和struct限制,C#編譯器會認為這是一個錯誤。因為這樣的指定是多餘的,所有值類型都隐式地提供了一個無參公共構造函數。就如同定義接口時指定通路類型為public一樣,編譯器也會報錯,因為接ロー定是public的,編譯器認為這樣做是多餘的。

轉換類型限制

T:基類名確定指定的類型實參必須是基類或派生自基類的子類;

T:接ロ名確定指定的類型實參必須是接ロ或實作了該接ロ的類;

T:U則確定T提供的類型實參必須是U提供的類型實參或派生于U提供的類型實參,即前面一個類型實參必須是後面的類型實參或後面類型實參的子類。

Class Sample<T> where T: Stream 
Sample<Stream>  √
Sample<string>   ×

Class Sample<T> where T: IDisposable  
Sample<Stream>   √
Sample<StringBuilder>  ×

Class Sample<T,U> where T: U  
Sample<Stream, IDispsable>  √
Sample<string, IDisposable>  ×
      

組合限制

  組合限制是将多個不同種類的限制合并在一起的情況。這裡需要注意,沒有任何一種類型既是引用類型,又是值類型,是以引用限制和值限制不能同時使用。如果存在多個轉換類型限制, 且其中一個是類,則類必須放在接口的前面。不同的類型參數可以有不同的限制,但每種類型參數必須分别使用一個單獨的where關鍵字。

有效的:

class Sample where T:class, IDisposable, new();

class Sample<T,U> where T:class where U: struct。

無效的:

class Sample where T: class, struct (沒有任何類型既是引用類型又是值類型,是以無效);

class Sample where T: Stream, class(引用類型限制應該為第一個限制,放在最前面,是以無效);

class Sample where T: new(), Stream (構造函數限制必須放在最後面,是以無效);

class Sample where T: IDisposable, Stream (類必須放在接口前面,是以無效);

class Sample<T,U> where T: struct where U:class, T ( 類型形參T具有struct限制,是以T不能用作U的限制, 是以無效);

class Sample<T,U> where T:Stream, U:IDisposable (不同的類型參數可以有不同的限制,但它們分别要有一個 單獨的where關鍵字,是以無效)。

協變與逆變

1、協變性:指的是泛型類型參數可以從一個派生類隐式地轉化為基類。使用out關鍵字來标記泛型參數,進而支援協變性。

2、逆變性:指的是泛型類型參數可以從一個基類隐式地轉化為派生類,使用in關鍵字來标記泛型參數,進而支援逆變性。

3、注意事項:

  并不是所有類型都支援泛型類型參數的協變和逆變性。

  • 隻有接口和委托才支援協變和逆變(如Func、Action),類或泛型方法的類型參數都不支援協變和逆變;
  • 協變和逆變隻适用于引用類型,值類型不支援協變和逆變(因為可變性存在引用轉換的過程,而值類型變量存儲的就是對象本身,并不是對象的引用),是以し151無法轉化為 Ienumerable
  • 必須顯式地用in或out來标記類型參數。
  • 委托的可變性不要在多點傳播委托中使用。

    Func stringfunc =()=>"";

繼續閱讀