Visual Basic 中的泛型型別 (Visual Basic)
「泛型型別」(Generic Type) 是單一程式設計項目,適用於針對多種資料型別執行相同的功能。 當您在定義泛型類別或程序時,不需要針對您想要執行該功能的每種資料型別定義不同的版本。
以下使用含有可更換頭的螺絲起子做比喻。 您會先檢查想要轉動的螺絲,然後針對該螺絲 (一字形、十字形或星字形) 選取正確的螺絲起子頭。 一旦您將正確的螺絲起子頭插入螺絲起子柄後,就可以用該螺絲起子執行相同的功能,也就是轉動螺絲。
設定為泛型工具的螺絲起子
當您定義泛型型別後,您就會以一或多個資料型別進行參數化。 這樣可讓使用中的程式碼根據需求調整資料型別。 您的程式碼可以從泛型項目宣告數種不同的程式設計項目,而每個項目都可以作用於不同的資料型別集。 但是不論宣告的項目是使用哪種資料型別,都會執行相同的邏輯。
例如,您可能想要建立和使用可在特定資料型別 (例如 String) 上運作的佇列類別。 您可以從 System.Collections.Generic.Queue<T> 宣告此種類別,如下列範例所示。
Public stringQ As New System.Collections.Generic.Queue(Of String)
您現在可以使用 stringQ,以獨佔模式與 String 值一起運作。 因為 stringQ 是 String 特有的,而不是通用於 Object 值,因此您沒有晚期繫結或型別轉換。 這可以節省執行時間並且減少執行階段錯誤。
如需使用泛型型別的詳細資訊,請參閱 HOW TO:使用泛型類別 (Visual Basic)。
泛型類別的範例
下列範例會顯示泛型類別的基本架構定義。
Public Class classHolder(Of t)
Public Sub processNewItem(ByVal newItem As t)
Dim tempItem As t
' Insert code that processes an item of data type t.
End Sub
End Class
在先前的基本架構中,t 為「型別參數」(Type Parameter),也就是您在宣告類別時所提供之資料型別的替代符號 (Placeholder)。 您可以在程式碼的其他地方,宣告各種的 classHolder 版本,方法便是提供 t 的各種資料型別。 以下範例顯示兩個此類宣告。
Public integerClass As New classHolder(Of Integer)
Friend stringClass As New classHolder(Of String)
先前的陳述式會宣告「建構的類別」(Constructed Class),其中特定型別會取代型別參數。 這項取代會遍及建構之類別內的程式碼。 下列範例會顯示 processNewItem 程序在 integerClass 中的外觀。
Public Sub processNewItem(ByVal newItem As Integer)
Dim tempItem As Integer
' Inserted code now processes an Integer item.
End Sub
如需更完整的範例,請參閱 HOW TO:定義可以在不同資料型別上提供完全相同功能的類別 (Visual Basic)。
適合的程式設計項目
您可以定義並使用泛型類別、結構、介面、程序和委派 (Delegate)。 請注意,.NET Framework 會定義數個代表通用泛型項目的泛型類別、結構和介面。 System.Collections.Generic 命名空間 (Namespace) 會提供字典、清單、佇列和堆疊。 在定義自己的泛型項目之前,請查看它是否已存在 System.Collections.Generic 中。
程序並非型別,但您可以定義和使用泛型程序。 請參閱 Visual Basic 中的泛型程序。
泛型型別的優點
泛型型別可以做為宣告數個不同程式設計項目的基礎,而每個項目會在特定的資料型別上運作。 泛型型別的替代型別如下:
在 Object 資料型別上運作的單一型別。
一組「特定型別」(Type-Specific) 的型別版本,每個版本都會個別進行編碼並於某一個特定的資料型別上運作,例如 String、Integer 或使用者定義的型別 (如 customer)。
泛型型別具有優於這些替代型別的下列優點:
型別安全:泛型型別會強制進行編譯時期型別檢查。 以 Object 為基礎的型別會接受任何資料型別,而且您必須撰寫程式碼以檢查輸入資料型別是否可接受。 利用泛型型別,編譯器 (Compiler) 可以在執行階段前攔截不符的型別。
效能:因為每個泛型型別都會針對某一種資料型別進行特製化,所以泛型型別不必進行資料的 Box 和 Unbox 處理。 根據 Object 的作業都必須進行輸入資料型別的 Box 處理,將這些資料型別轉換為 Object,並針對預定輸出的資料進行 Unbox 處理。 Box 和 Unbox 處理都會降低效能。
以 Object 為基礎的型別也屬於晚期繫結的型別,這表示在執行階段存取這些型別的成員時,還需要額外的程式碼。 這也會降低效能。
程式碼合併:泛型型別中的程式碼只能夠定義一次。 一組型別的特定型別版本必須在每個版本中複寫相同的程式碼,唯一的差異在於該版本的特定資料型別。 就泛型型別而言,特定型別的版本都是從原始泛型型別產生。
重複使用程式碼:未相依於特殊資料型別的程式碼可以重複使用於各種資料型別 (如果是泛型型別的話)。 通常甚至可以重複使用於您原先未預期的資料型別。
IDE 支援:當您使用從泛型型別宣告之建構的型別時,整合式開發環境 (IDE) 便可以於開發程式碼時給予更多支援。 例如, IntelliSense 會顯示您引數的型別特有選項傳遞給建構函式或方法。
泛型演算法:與型別無關的抽象演算法可以做為良好的泛型型別。 例如,使用 IComparable 介面排序項目的泛型程序,可以用於實作 IComparable 的任何資料型別。
條件約束
雖然泛型型別定義中的程式碼應該盡可能與型別無關,但您可能會要求提供給泛型型別的任何資料型別具有某項功能。 例如,如果您想藉由比較兩個項目來達到排序或定序的目的,這兩個項目的資料型別就必須實作 IComparable 介面。 您可以將「條件約束」(Constraint) 加入至型別參數,以強制這項要求。
條件約束範例
下列範例會顯示類別的基本架構定義,該類別具有要求型別引數實作 IComparable 的條件約束。
Public Class itemManager(Of t As IComparable)
' Insert code that defines class members.
End Class
如果後續的程式碼嘗試從提供未實作 IComparable 之型別的 itemManager 建構類別,則編譯器會發出錯誤信號。
條件約束型別
您的條件約束可以利用任意組合指定下列需求:
型別引數必須實作一或多個介面。
型別引數必須為某個類別的型別,或繼承自至多一個類別。
型別引數必須公開 (Expose) 無參數的建構函式,此建構函式可供據此建立物件的程式碼存取。
型別引數必須是「參考型別」(Reference Type),或必須是「實值型別」(Value Type)。
如果您需要強制一個以上的要求,可以在大括號 ({ }) 內使用逗號分隔的「條件約束清單」(Constraint List)。 如需要求可存取的建構函式,可以在清單中包含 New 運算子 (Visual Basic) 關鍵字。 如需要求參考型別,您可以包含 Class 關鍵字。而若要要求實值型別,則可以包含 Structure 關鍵字。
如需條件約束的詳細資訊,請參閱型別清單 (Visual Basic)。
多重條件約束的範例
下列範例會顯示泛型類別的基本架構定義,此類別具有型別參數的條件約束清單。 在建立此類別之執行個體的程式碼中,型別引數必須是參考型別,且必須實作 IComparable 和 IDisposable 介面,並公開可存取的無參數建構函式。
Public Class thisClass(Of t As {IComparable, IDisposable, Class, New})
' Insert code that defines class members.
End Class
重要詞彙
泛型型別會引入並使用下列詞彙:
泛型型別: 類別、結構、介面、程序或委派的定義,您會在進行宣告時提供至少一個資料型別。
型別參數: 在泛型型別定義中,您在宣告型別時所提供之資料型別的替代符號 (Placeholder)。
型別引數: 從泛型型別宣告建構的型別時,取代型別參數的特定資料型別。
條件約束: 型別參數的條件,會限制您可以提供的型別引數。 條件約束可以要求型別引數必須實作特定介面、本身必須為或是必須繼承自特定類別、具有可存取的無參數建構函式,或為參考型別或實值型別。 您可以結合這些條件約束,但最多只能指定一個類別。
建構的型別: 藉由提供型別參數的型別引數,從泛型型別宣告的類別、結構、介面、程序或委派。