跟小靜讀CLR via C#(16)–泛型

來源:互聯網
上載者:User

泛型就像是一個模板,常常定義一些通用的演算法,具體調用時再替換成實際的資料類型,提高了代碼的可重用性。

一、初識泛型1. 簡單一實例

以最常用的FCL中的泛型List<T >為例:

static void Main(string[] args)
       {
           List<int> num = new List<int>();
           num.Add(1);
           num.Add(3);
           int num1 = num[0];
           int num2 = num[1];
       }

角括弧中的T是不確定的資料類型,叫做型別參數,一般規定以字母T開頭,可以是TKey, TValue都可以。而調用時指定的具體類型叫做類型實參。

查看一下IL代碼:

  • 類型名List是以“`”加數字結尾的。數字表示類型的元數,也就是需要指定具體類型的參數個數。
  • 泛型是型別安全的。如果用“num.Add("a");”會發生編譯錯誤;
  • 泛型可以提高演算法的可重用性,而且從例子中看出int類型並沒有進行裝箱拆箱操作,相比將所有類型轉換為Object的方式而言,提高了程式的效能。
  • 為泛型變數設定預設值時常使用default關鍵字進行,T temp=default(T)。如果T為參考型別,則temp為null;如果T為實值型別,則temp設為0值.
2. 開放類型與封閉類型:

開放類型:具有泛型參數的類型是開放類型,如List<T>,CLR不允許構造開放類型的執行個體;

封閉類型:在實際調用代碼時,如果所有類型實參都已經指定了實際資料類型,如List<string>,則該類型為封閉類型。CLR允許構造封閉類型的執行個體。

3. 類型推斷:

先看這段很常見的代碼:

為了增強可讀性,編譯器支援類型推斷功能,省略<>,我們可以將上面調用的方法改為:

* 需要注意的是,類型推斷時C#使用的是變數的資料類型,而不是變數引用的對象的類型。例如:

雖然s1和s2都是指向了字串對象,但是這兩個變數的類型是不同的,所以會產生編譯錯誤。

二、協變和逆變泛型型別參數

通過協變數和逆變數,可以將泛型委派或者介面的型別參數進行一定的類型轉換。

  • 逆變數:泛型型別參數可以從基類轉為衍生類別,用in關鍵字標識,只出現在輸入位置,例如方法的參數;

public delegate void Func<in T>(T arg);

static void Main(string[] args)

{

Func<object> f1 = null;

Func<string> f2 = f1;

}

  • 協變數:泛型型別參數可以從衍生類別改為它的基類,用out關鍵字標識,只出現在輸出位置,例如方法的傳回值。

public delegate TResult Func<out TResult>();

static void Main(string[] args)

{

Func<string> fn=null;

object result=fn();

}

三、泛型約束

在設計泛型的型別參數時,可以通過where子句指定類型需要滿足的約束條件。主要包含以下幾種約束方式:

1. 主要約束

一個型別參數可以指定0或1個主要約束,主要約束可以一個非密封的參考型別,它表示類型實參必須與約束類型相同或者為約束類型的衍生類別。該參考型別不能為Object, Array, Delegate, MulticastDelegate, ValueType, Enum, Void。

class Constraint1<T> where T : Stream
{
     public void Close(T stream)
     {
         stream.Close();
     }
}

class Program
{
     static void Main(string[] args)
     {
         Constraint1<FileStream> s2 = new Constraint1<FileStream>();
     }
}

兩種特殊的主要約束:class和struct。

  • Class約束:要求指定的類型實參必須是參考型別。Where T:class

在沒有約束的情況下,如果T為實值型別,是不能賦值為null的,所以會產生編譯錯誤。添加約束後編譯通過:

  • Struct 約束:要求指定的類型實參必須是實值型別

在沒有約束的情況下,如果T為參考型別是不能聲明為可空實值型別的,所以會產生編譯錯誤。添加struct約束後運行正常:

2. 次要約束

一個型別參數可以指定0或者多個次要約束。常見的次要約束主要有兩種:

  • 介面約束:類型實參必須實現了指定的所有介面。例如:

介面約束的另外一個好處是:實值型別實參調用介面方法時不用進行裝箱操作。

  • 型別參數約束:在指定的類型實參之間,存在著一定關係。例如要求存在繼承關係:

3. 構造器約束

構造器約束要求類型實參必須實現了無參構造器,而且它不支援有參構造器。

 

你也許喜歡:跟小靜讀CLR via C#(00)-開篇及目錄

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.