Remove ads
设计和编写程序,用参数类型编写算法,易于重用。 来自维基百科,自由的百科全书
泛型程式設計(英文:generic programming)是程式語言的一種風格或範式。泛型允許程式設計師在強型別程式語言中編寫代碼時使用一些以後才指定的類型,在實例化時作為參數指明這些類型。各種程式語言和其編譯器、執行環境對泛型的支援均不同。Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Visual Basic .NET 稱之為泛型(generics);ML、Scala 和 Haskell 稱之為參數多型(parametric polymorphism);C++ 和 D稱之為模板。具有廣泛影響的1994年版的《Design Patterns》一書稱之為參數化類型(parameterized type)。
此條目的語調或風格或許不適合百科全書。 (2020年9月21日) |
此條目可參照英語維基百科相應條目來擴充。 (2020年9月21日) |
泛型的定義主要有以下兩種:
不論使用哪個定義,泛型的參數在真正使用泛型時都必須作出指明。
類 例泛類<T> {
值 : T
設置值(新值 : T) {
值 := 新值
}
獲取值() : T {
返回 值
}
}
例方法1() {
例物件 : 例泛類<整數型>
例物件 := 新 例泛類<整數型>()
例物件.設置值(5)
輸出整數(例物件.獲取值())
}
例方法2() {
例物件 : 例泛類<浮點數型>
例物件 := 新 例泛類<浮點數型>()
例物件.設置值(5.5)
輸出浮點數(例物件.獲取值())
}
在這例子中,例泛類
是一個泛型,而T
是一個類型參數。在例泛類
中沒指明T
的實際類型,只有例方法1()
和例方法2()
在使用例泛類
時才加以指明。
運行這例子的例方法1()
將輸出整數5,而運行例方法2()
將輸出浮點數5.5。
.NET 泛型的參數只可以代表类别,不能代表個別物件。由於 .NET 泛型的類型參數之實際類型在執行時均不會被消除,執行速度會因為類型轉換的次數減少而加快。另外,使用GetType()
方法可於程式執行時得知泛型及其類型參數的實際類型,更可以運用反射式編程。
using System;
// 定義一個泛型列表類,T 表示類型參數
public class GenericList<T>
{
private T[] _items; // 存儲列表items的數組
private int _count; // 列表中items的計數
// 構造函數,初始化列表的容量
public GenericList(int capacity)
{
_items = new T[capacity];
_count = 0;
}
// 添加item到列表中
public void Add(T item)
{
if (_count < _items.Length)
{
_items[_count] = item;
_count++;
}
else
{
Console.WriteLine("List capacity reached."); // 列表容量已滿
}
}
// 根據索引獲取列表中的item
public T GetItem(int index)
{
if (index >= 0 && index < _count)
{
return _items[index];
}
else
{
throw new IndexOutOfRangeException("Index out of range."); // 索引超出範圍
}
}
}
public class Program
{
public static void Main()
{
// 創建一個存儲整數的泛型列表
GenericList<int> intList = new GenericList<int>(3);
intList.Add(1);
intList.Add(2);
intList.Add(3);
Console.WriteLine(intList.GetItem(1)); // 輸出: 2
// 創建一個存儲字符串的泛型列表
GenericList<string> stringList = new GenericList<string>(2);
stringList.Add("Hello");
stringList.Add("World");
Console.WriteLine(stringList.GetItem(0)); // 輸出: Hello
}
}
在上面的例子中展示了一個簡單的泛型列表類 GenericList<T>
,它可以儲存任何類型的數據(由 T
指定)。Program
類中的 Main
方法演示了如何使用這個泛型類來儲存和檢索整數和字串。
.NET 允許對個別泛型的類型參數進行約束,包括以下幾種形式[1](假設T
是泛型的類型參數,C
是一般類別、泛類,或是泛型的類型參數):
T
是一個類別。T
是一個值類型。T
具有無參數的公有建構方法。T
實現界面I
。T
是C
,或繼承自C
。Java 泛型的參數只可以代表类别,不能代表個別物件。由於Java泛型的類型參數之實際類型在編譯時會被消除,所以無法在執行時得知其類型參數的類型,而且無法直接使用基本值類型作為泛型類型參數。Java編譯程式在編譯泛型時會自動加入類型轉換的編碼,故執行速度不會因為使用泛型而加快。
由於執行時會消除泛型的對象實例類型資訊等缺陷經常被人詬病,Java及JVM的開發方面也嘗試解決這個問題,例如:Java通過在生成位元組碼時添加類型推導輔助資訊,從而可以通過反射介面獲得部分泛型資訊;通過改進泛型在JVM的實現,使其支援基本值類型泛型和直接獲得泛型資訊等。
Java允許對個別泛型的類型參數進行約束,包括以下兩種形式[2](假設T
是泛型的類型參數,C
是一般類別、泛類,或是泛型的類型參數):
T
實現介面I
。T
是C
,或繼承自C
。C++ 泛型的參數可以代表类别或個別物件。在一般意義上,C++ 缺乏對泛型的類型參數進行直接約束的手段,但可利用 SFINAE(模板代換失敗非錯誤,指在模板實例化過程中的錯誤僅意味此次代換失敗,並不一定產生編譯錯誤)規則及 C++11 的 static_assert 等實現相似功能。
#include <type_traits>
class B{
...
};
class D: public B{
...
};
template<typename T>
void SFINAE(const std::enable_if_t<std::is_base_of<B, T>::value, T> &t);
template<typename T>
void STATIC_ASSERT(const T &t){
static_assert(std::is_pod<T>::value, "Use with POD types only!");
}
如上所示,std::enable_if(std::enable_if_t<boolean, Type> 是 std::enable_if<boolean, Type>::type 的縮寫)利用 SFINAE 規則來實現模板類型參數約束的手段之一。其實現方式是若布林判斷為假,則把類型設為 void,而這將導致 const void & 這種不合法的類型出現,從而禁止這種類型參數的使用。
static_assert 則在布林判斷失敗時把後面的字串作為訊息內容報告為編譯錯誤。
在編譯時,每個被使用的封閉泛型類型(即是所有泛型參數的實際類型都已被指明的泛型)都會有獨立的編碼產生,編譯程式會在此時確保類型安全性。可是如果泛型要運用其泛型參數的某成員,而該泛型參數又不包含該成員的時候,編譯程式所產生的錯誤資訊或會看似與實際問題無關,增加除錯的難度。
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.