.NET Framework 2.0でジェネリクスがサポートされたことに伴い、VB8(VB2005)以降ではVBでもジェネリクスを使用できるようになりました。 ジェネリクス(generics)とは何かを端的に述べると、ある機能を持つクラスやメソッドを、任意の型に対して適用できるようにするものです。
メソッド(プロシージャ)やクラスでは、何らかの動作や機能が定義されていて、その機能を指定された引数に対して適用することができます。 ジェネリックメソッド・ジェネリッククラスも何らかの機能が定義されている点は同様ですが、その機能を指定された型に対して適用できるようにするのがジェネリクスの機能です。 通常のメソッド・クラスで提供される機能は多くの場合何らかの型に依存することになりますが、ジェネリクスを使うと型に依存せず機能を提供することができるようになります。 様々な型に適用できる汎用的な機能を実装する際にジェネリクスを使うことができます。
例えば、コレクションクラスは複数のオブジェクトをまとめて管理し、追加・検索・削除などを行う機能を持っています。 ジェネリックなコレクションクラスでは、これらの機能を任意の型に対して適用できるようになっています。 そのため、ジェネリックなコレクションクラスを使うことで、String用のコレクション・Integer用のコレクションなど型に応じたコレクションクラスを個別に作成する必要がなくなります。
ジェネリクスの例
ジェネリックコレクション
ジェネリクスの有用な例として、ジェネリックコレクションがあります。 .NET FrameworkではSystem.Collections.Generic名前空間にさまざまなジェネリックなコレクションクラスが用意されています。
次の例は、ジェネリックな可変長リストであるListクラスを使って、String型のコレクションを作成し、要素を追加しています。
ジェネリッククラスでは、クラス名(Of 型)
とすることで、その型に型付けされたクラスを構築することができます。 例えば、ジェネリッククラスであるListの場合では、List(Of String)
とすればString用のList、List(Of Integer)
とすればInteger用のListを作成することができます。 Of
句によってジェネリッククラスに与えられる型は型パラメータと呼ばれます。
型パラメータによって型付けされたクラスでは、その型、もしくはその型と互換性のある型・拡大変換・暗黙の型変換が行える型のみが扱えます。
例えば、List(Of Integer)
、つまりInteger型に片付けされたListでは、Integer型かInteger型に変換できる値(ShortやByte)のみが扱えます。 それ以外の型の場合を扱おうとすると、コンパイル時(Option Strict Onの場合)または実行時(Option Strict Offの場合)にエラーとなります。
Listクラス以外にも、System.Collections.Generic名前空間にはさまざまなジェネリックコレクションクラスが用意されています。 詳しくはジェネリックコレクションを参照してください。
ジェネリックメソッド
ジェネリクスは型の単位だけでなく、メソッド単位で適用することもできます。 ジェネリック型ではクラスや構造体などの型単位で型指定が行われますが、ジェネリックメソッドではメソッド単位で型指定が行われます。 このようなジェネリックメソッドの一例として、Array.Resizeメソッドがあります。
Array.Resizeメソッドは引数で与えられた配列のサイズを変更するものですが、配列の型を限定するために型パラメータが用いられます。 Integer型の配列を指定する場合はArray.Resize(Of Integer)
、String型の配列の場合はArray.Resize(Of String)
とします。
このように、ジェネリックメソッドではメソッド名(Of 型)(引数1, 引数2, ...)
とすることで、指定した型に限定されたメソッドを呼び出すことができます。
なお、コンパイラが適切な型を推定できる(型推論が行える)場合は、コンパイラが自動的に適切な型を選ぶため型パラメータの指定を省略することもできます。 例えば、先の例では引数に指定された配列の型から呼び出すべきジェネリックメソッドの型パラメータを推測できるため、次のように型パラメータの指定を省略して呼び出すこともできます。
ジェネリックメソッドの宣言
ここまでは既存のジェネリック型・ジェネリックメソッドを使用する例を見てきましたが、独自に定義することもできます。 ここでは二つの引数を入れ替えるジェネリックメソッドSwap(x, y)を作成することを考えます。 まずはジェネリクスを使わず通常のメソッドで実装すると、次のように引数の型ごとにメソッドを用意する必要があります。
このSwapメソッドをジェネリックメソッドにすると次のようになります。 IntegerやStringといった具体的な型の代わりに、型パラメータTを引数の型とすることでどのような型に対しても呼び出せるようにします。 呼び出し側では型パラメータTに具体的な型を指定します。 この例では型推論によって型パラメータに指定されるべき型が推測できるので、型パラメータの指定を省略することもできます。
このようにSub|Function メソッド名(Of 型パラメータ)(引数リスト)
とすることでジェネリックメソッドを宣言することができます。
ジェネリック型の宣言
クラスや構造体などをジェネリックにする場合は、Class|Structure 型名(Of 型パラメータ)
とします。 クラス・構造体内では、具体的な型名の代わりに型パラメータで指定した型名を使用できるようになります。
ジェネリック型の継承
ジェネリック型を継承して新たに型を作成することもできます。 System.Collections.ObjectModel名前空間にはCollectionなどのさまざまな目的に使えるジェネリックなコレクションクラスが用意されているため、これを継承して独自のコレクション型を作成することもできます。
型パラメータの制約
ジェネリック型・ジェネリックメソッドの宣言する際、Of句でOf T
とした場合、型パラメータTには任意の型を指定することができます。 型パラメータで指定できる型に制約を設けることもできます。 例えば値型もしくは参照型に限定したり、何らかのインターフェイスを実装している型に限定したり、といったことが出来ます。
制約について詳しくは制約で解説しています。