<p><b>.NET Framework泛型</b>是在.NET Framework 2.0的公共語言運行庫（CLR）中的增加的一項新功能，類似於C++的模板，但不如C++的模板靈活，不過也有一些自己的特性。
</p><p>泛型為.NET Framework引入了類型參數的概念，這樣便可以設計出這樣的類和方法：它們把指定類型的工作推遲到客戶端代碼聲明並實例化類或方法的時候執行。比如，通過泛型類型參數T，程式設計師就可以編寫其他客戶端代碼能夠使用的單個類，而不用擔心強制轉換或裝箱操作而帶來的額外的運行成本或風險。
</p>


<h2><span id=".E6.B3.9B.E5.9E.8B.E7.BC.96.E7.A8.8B"></span><span id="泛型编程">泛型編程</span></h2>
<p>泛型類和方法較之非泛型類和方法具有更高的可用性、類型安全和效率，且通常用在集合和集合上運行的方法中。在.NET Framework2.0的類庫中添加了一個新的命名空間System.Collections.Generic，其中含有一些新的基於泛型的集合類。微軟建議程式設計師在設計面向.NET Framework 2.0的應用程式時不要使用舊的非泛型集合類（像ArrayList集合類），而去使用新的泛型集合類。
</p><p>當然，程式設計師也可以創建自己的泛型類和方法，提供自己需要的解決方案，但微軟不推薦自己創建泛型類，而建議使用.NET Framework類庫中提供的List&lt;T&gt;類。
</p><p>可以通過泛型類型參數傳遞一個類型（如一個類TestClass&lt;T&gt;）中的T是泛型的類型參數，可以通過它傳遞一個類型（如int），那麼，在這個類中所有用類型參數（在本例中是T）定義的欄位或方法等的類型都會根據傳遞進來的類型（本例中為int類型）所改變。
</p>
<h3><span id=".E6.B3.9B.E5.9E.8B.E7.B1.BB.E5.9E.8B.E5.8F.82.E6.95.B0"></span><span id="泛型类型参数">泛型類型參數</span></h3>
<p>在定義泛型類或方法中，類型參數是客戶端程序實例化泛型類變量時指定的類型占位符。泛型類不能像通常的類那樣使用，因為它實際上並不是一個類型，可能更像一個類型的藍圖。要使用泛型類，客戶端代碼必須要指定尖括號中的類型參數並實例化類型才能構造類型。指定的類型參數可以是編譯器可以識別的任何類型，並且可以構造任意數目的使用不同類型參數的實例。
</p>
<h4><span id=".E5.91.BD.E5.90.8D.E8.A7.84.E5.88.99"></span><span id="命名规则">命名規則</span></h4>
<p>通常，微軟推薦採用如下命名規則：
</p>
<ul><li>除非單個字母可以讓其他編寫者了解其含義，而描述性名稱並不能帶來更多其他的意義，應該使用描述性短語作為類型參數的名稱</li>
<li>推薦使用T作為單個字母類型參數的參數名</li>
<li>應該將T作為有描述性的類型參數名前綴</li>
<li>推薦在類型參數名中指出對這個類型參數的約束（如將帶有ISession約束的參數名寫為TSession）。</li></ul><h3><span id=".E6.B3.9B.E5.9E.8B.E5.8F.82.E6.95.B0.E7.9A.84.E7.BA.A6.E6.9D.9F"></span><span id="泛型参数的约束">泛型參數的約束</span></h3>
<p>對於泛型參數的約束是指對客戶端代碼實例化類時指定的類型參數進行限制，這樣如果客戶端的程序參試使用某個不允許使用的類型實例化類時會產生編譯時錯誤。
</p><p>以C# 為例，約束採用where上下文關鍵字指定。
</p>
<h4><span id=".E4.B8.BA.E4.BB.80.E4.B9.88.E4.BD.BF.E7.94.A8.E7.BA.A6.E6.9D.9F"></span><span id="为什么使用约束">為什麼使用約束</span></h4>
<p>若要對泛型列表中的項與其他某個項進行比較，便需要在一定程度上保證所調用的運算符或方法可以得到指定的類型參數的支持。這個保證正是建立在對泛型類定義的一個或多個約束獲得的。一旦編譯器得到了這樣的保證，它就能夠允許在泛型類中調用一些無約束的泛型中不允許使用的方法。
</p>
<h4><span id=".E6.9C.AA.E7.BB.91.E5.AE.9A.E7.9A.84.E7.B1.BB.E5.9E.8B.E5.8F.82.E6.95.B0"></span><span id="未绑定的类型参数">未綁定的類型參數</span></h4>
<p>沒有進行約束的泛型類型參數稱為未綁定的類型參數。
</p><p>當使用未綁定的類型參數時，相對於使用了約束的類型參數，有以下規則：
</p>
<ul><li>不允許使用!=和==運算符，因為編譯器無法保證獲得實例化時賦予的類型參數的支持；但可以與null進行比較。如果類型參數為值類型的話，一定返回false。</li>
<li>可以與System.Object將互相轉換，也可以將它們顯式地轉換為任何藉口類型</li></ul><h4><span id=".E8.A3.B8.E7.B1.BB.E5.9E.8B.E7.BA.A6.E6.9D.9F"></span><span id="裸类型约束">裸類型約束</span></h4>
<h3><span id=".E6.B3.9B.E5.9E.8B.E7.B1.BB"></span><span id="泛型类">泛型類</span></h3>
<p>泛型類可以擁有至少一個泛型類型參數，泛型類型參數可以代替泛型類的欄位、屬性、方法的參數和返回值、事件的參數和返回值、構造函數的參數的類型，也可作為內部類和基類以及實現接口的泛型類型參數。
</p><p>泛型類必須在創建其實例時明確指定其所有泛型類型參數的類型。泛型類的構造函數不能自動推斷其泛型類型的泛型類型參數。
</p><p>如果從泛型類派生，可以選擇確定的泛型類型參數，也可以用泛型派生類，利用泛型派生類的泛型類型參數確定基類的泛型類型參數。
</p>
<h3><span id=".E6.B3.9B.E5.9E.8B.E6.8E.A5.E5.8F.A3"></span><span id="泛型接口">泛型接口</span></h3>
<p>泛型接口的使用大體上與泛型類相同，只是接口不允許包含欄位和構造函數，也不允許創建實例。實現接口時，同樣也可以選擇實現確定的泛型類型接口，或者利用泛型類型參數確定接口的泛型類型參數。
</p>
<h3><span id=".E6.B3.9B.E5.9E.8B.E6.96.B9.E6.B3.95"></span><span id="泛型方法">泛型方法</span></h3>
<p>泛型方法可以擁有至少一個泛型類型參數，泛型類型參數可以作為方法的參數或返回值的類型。泛型方法不用明確指定各泛型類型參數，可由上下文自動推斷，如果上下文無法自動推斷，則必須明確指定。
</p>
<h3><span id=".E6.B3.9B.E5.9E.8B.E5.A7.94.E6.89.98"></span><span id="泛型委托">泛型委託</span></h3>
<p>泛型委託的使用大體上與泛型方法相同，泛型委託的泛型類型參數可以在創建時通過上下文自動推斷。泛型委託實例總是具有確定的泛型類型參數。
</p>
<h3><span id=".E5.8F.8D.E5.B0.84"></span><span id="反射">反射</span></h3>
<h2><span id=".E6.B3.9B.E5.9E.8B.E5.92.8CC.2B.2B.E4.B8.AD.E6.A8.A1.E6.9D.BF.E7.9A.84.E5.8C.BA.E5.88.AB"></span><span id="泛型和C++中模板的区别">泛型和C++中模板的區別</span></h2>
<p>泛型和模板都是用於提供類型參數支持的語言功能，但這兩者有著諸多差異。
</p><p>從語法層面上來說，一般認為泛型是一種相對模板簡單的方法，而不具有模板的複雜性。但同時，泛型也不能提供模板的完整功能。
</p><p>另一方面，從實現層面，泛型的替換是在運行時執行的，這樣可以為實例化的對象保留泛型類型的信息。
</p><p>下面列出了.NET Framework泛型和C++模板之間的主要區別：
</p>
<ul><li>泛型沒有提供與模板相同程度的靈活性（如泛型不能調用算數運算符）</li>
<li>泛型不允許非類型模板參數（如template C&lt;int i&gt;{}）</li>
<li>泛型不支持顯式的特定類型模板的自定義實現</li>
<li>泛型不支持類型參數子集的自定義實現</li>
<li>泛型中不允許將類型參數作泛型類型的基類</li>
<li>泛型不允許類型參數具有默認類型，而必須在實例化時指定</li></ul><p>泛型類型參數自身不能是泛型，但C++確實允許模板參數。
</p><p>C++允許程式設計師編寫可能對模板中某些類型參數無效的代碼，然後將檢查該代碼中是否有用作類型參數的特定類型。比如說可以在C++中編寫對類型參數對象的算術運算符的函數，這樣在用不支持這些運算符的類型實例化模板時出現出現錯誤；而泛型是不允許這樣的，它則要求相應地編寫類當中的代碼，使他們能夠滿足任何約束的類型。因此唯一允許的語言構造是那些能夠從約束中推導出來的結構。
</p>
<h2><span id=".E5.8F.82.E7.9C.8B"></span><span id="参看">參看</span></h2>
<ul><li>C#</li>
<li>C++</li>
<li>類庫</li>
<li>.NET Framework</li>
<li>類型參數</li>
<li>接口</li>
<li>委託</li></ul><h3><span id="System.Collections.Generic.E5.91.BD.E5.90.8D.E7.A9.BA.E9.97.B4"></span><span id="System.Collections.Generic命名空间">System.Collections.Generic命名空間</span></h3>
<h4><span id=".E7.B1.BB"></span><span id="类">類</span></h4>
<h4><span id=".E6.8E.A5.E5.8F.A3"></span><span id="接口">接口</span></h4>
<h4><span id=".E7.BB.93.E6.9E.84"></span><span id="结构">結構</span></h4>
<h2><span id=".E5.A4.96.E9.83.A8.E9.93.BE.E6.8E.A5"></span><span id="外部链接">外部連結</span></h2>
<ul><li>C#語言規範（英文）</li>
<li>.NET Framework 2.0（中文）</li></ul>
**.NET Framework 泛型**是在.NET Framework 2.0 的公共語言運行庫（CLR）中的增加的一項新功能，類似於 C++ 的模板，但不如 C++ 的模板靈活，不過也有一些自己的特性。

泛型為.NET Framework 引入了類型參數的概念，這樣便可以設計出這樣的類和方法：它們把指定類型的工作推遲到客戶端代碼聲明並實例化類或方法的時候執行。比如，通過泛型類型參數 T，程式設計師就可以編寫其他客戶端代碼能夠使用的單個類，而不用擔心強制轉換或裝箱操作而帶來的額外的運行成本或風險。

## 泛型編程

泛型類和方法較之非泛型類和方法具有更高的可用性、類型安全和效率，且通常用在集合和集合上運行的方法中。在.NET Framework2.0 的類庫中添加了一個新的命名空間 System.Collections.Generic，其中含有一些新的基於泛型的集合類。微軟建議程式設計師在設計面向.NET Framework 2.0 的應用程式時不要使用舊的非泛型集合類（像 ArrayList 集合類），而去使用新的泛型集合類。

當然，程式設計師也可以創建自己的泛型類和方法，提供自己需要的解決方案，但微軟不推薦自己創建泛型類，而建議使用.NET Framework 類庫中提供的 List<T> 類。

可以通過泛型類型參數傳遞一個類型（如一個類 TestClass<T>）中的 T 是泛型的類型參數，可以通過它傳遞一個類型（如 int），那麼，在這個類中所有用類型參數（在本例中是 T）定義的欄位或方法等的類型都會根據傳遞進來的類型（本例中為 int 類型）所改變。

### 泛型類型參數

在定義泛型類或方法中，類型參數是客戶端程序實例化泛型類變量時指定的類型占位符。泛型類不能像通常的類那樣使用，因為它實際上並不是一個類型，可能更像一個類型的藍圖。要使用泛型類，客戶端代碼必須要指定尖括號中的類型參數並實例化類型才能構造類型。指定的類型參數可以是編譯器可以識別的任何類型，並且可以構造任意數目的使用不同類型參數的實例。

#### 命名規則

通常，微軟推薦採用如下命名規則：

* 除非單個字母可以讓其他編寫者了解其含義，而描述性名稱並不能帶來更多其他的意義，應該使用描述性短語作為類型參數的名稱
* 推薦使用 T 作為單個字母類型參數的參數名
* 應該將 T 作為有描述性的類型參數名前綴
* 推薦在類型參數名中指出對這個類型參數的約束（如將帶有 ISession 約束的參數名寫為 TSession）。

### 泛型參數的約束

對於泛型參數的約束是指對客戶端代碼實例化類時指定的類型參數進行限制，這樣如果客戶端的程序參試使用某個不允許使用的類型實例化類時會產生編譯時錯誤。

以 C# 為例，約束採用 where 上下文關鍵字指定。

#### 為什麼使用約束

若要對泛型列表中的項與其他某個項進行比較，便需要在一定程度上保證所調用的運算符或方法可以得到指定的類型參數的支持。這個保證正是建立在對泛型類定義的一個或多個約束獲得的。一旦編譯器得到了這樣的保證，它就能夠允許在泛型類中調用一些無約束的泛型中不允許使用的方法。

#### 未綁定的類型參數

沒有進行約束的泛型類型參數稱為未綁定的類型參數。

當使用未綁定的類型參數時，相對於使用了約束的類型參數，有以下規則：

* 不允許使用！= 和 == 運算符，因為編譯器無法保證獲得實例化時賦予的類型參數的支持；但可以與 null 進行比較。如果類型參數為值類型的話，一定返回 false。
* 可以與 System.Object 將互相轉換，也可以將它們顯式地轉換為任何藉口類型

#### 裸類型約束

### 泛型類

泛型類可以擁有至少一個泛型類型參數，泛型類型參數可以代替泛型類的欄位、屬性、方法的參數和返回值、事件的參數和返回值、構造函數的參數的類型，也可作為內部類和基類以及實現接口的泛型類型參數。

泛型類必須在創建其實例時明確指定其所有泛型類型參數的類型。泛型類的構造函數不能自動推斷其泛型類型的泛型類型參數。

如果從泛型類派生，可以選擇確定的泛型類型參數，也可以用泛型派生類，利用泛型派生類的泛型類型參數確定基類的泛型類型參數。

### 泛型接口

泛型接口的使用大體上與泛型類相同，只是接口不允許包含欄位和構造函數，也不允許創建實例。實現接口時，同樣也可以選擇實現確定的泛型類型接口，或者利用泛型類型參數確定接口的泛型類型參數。

### 泛型方法

泛型方法可以擁有至少一個泛型類型參數，泛型類型參數可以作為方法的參數或返回值的類型。泛型方法不用明確指定各泛型類型參數，可由上下文自動推斷，如果上下文無法自動推斷，則必須明確指定。

### 泛型委託

泛型委託的使用大體上與泛型方法相同，泛型委託的泛型類型參數可以在創建時通過上下文自動推斷。泛型委託實例總是具有確定的泛型類型參數。

### 反射

## 泛型和 C++ 中模板的區別

泛型和模板都是用於提供類型參數支持的語言功能，但這兩者有著諸多差異。

從語法層面上來說，一般認為泛型是一種相對模板簡單的方法，而不具有模板的複雜性。但同時，泛型也不能提供模板的完整功能。

另一方面，從實現層面，泛型的替換是在運行時執行的，這樣可以為實例化的對象保留泛型類型的信息。

下面列出了.NET Framework 泛型和 C++ 模板之間的主要區別：

* 泛型沒有提供與模板相同程度的靈活性（如泛型不能調用算數運算符）
* 泛型不允許非類型模板參數（如 template C<int i>{}）
* 泛型不支持顯式的特定類型模板的自定義實現
* 泛型不支持類型參數子集的自定義實現
* 泛型中不允許將類型參數作泛型類型的基類
* 泛型不允許類型參數具有默認類型，而必須在實例化時指定

泛型類型參數自身不能是泛型，但 C++ 確實允許模板參數。

C++ 允許程式設計師編寫可能對模板中某些類型參數無效的代碼，然後將檢查該代碼中是否有用作類型參數的特定類型。比如說可以在 C++ 中編寫對類型參數對象的算術運算符的函數，這樣在用不支持這些運算符的類型實例化模板時出現出現錯誤；而泛型是不允許這樣的，它則要求相應地編寫類當中的代碼，使他們能夠滿足任何約束的類型。因此唯一允許的語言構造是那些能夠從約束中推導出來的結構。

## 參看

* C#
* C++
* 類庫
* .NET Framework
* 類型參數
* 接口
* 委託

### System.Collections.Generic 命名空間

#### 類

#### 接口

#### 結構

## 外部連結

* C# 語言規範（英文）
* .NET Framework 2.0（中文）
.NET_Framework_泛型