Visual Basic におけるジェネリック型 (Visual Basic)
ジェネリック型はさまざまなデータ型に対して同じ機能を実行するために必要な処理を行う、1 つのプログラミング要素です。ジェネリック クラスまたはジェネリック プロシージャを定義すると、同じ機能を実行させる各データ型に対して、その機能を別々に定義する必要がありません。
これは、ヘッドの部分が交換可能な、ねじ回しのセットにたとえることができます。回すねじを調べて、そのねじに合った正しいヘッド (マイナス、プラス、星型) を選択します。ねじ回しのハンドルに正しいヘッドを挿入したら、ねじ回しを使ってまったく同じ作業 (ねじを回すこと) を行います。
汎用的な道具であるねじ回しのセット
ジェネリック型を定義するには、1 つ以上のデータ型でジェネリック型をパラメーター化します。これにより、ジェネリック型を使用するコードで、データ型をコードの要件に合わせて変更できるようになります。コードでは、1 つのジェネリックな要素から複数のプログラミング要素を宣言し、それぞれを異なるデータ型に使用できます。ただし、使用するデータ型が異なっていても、宣言した要素はどれも同じロジックを実行します。
たとえば、String などの特定のデータ型を操作するキュー クラスを作成し、使用する必要があるとします。次の例に示すように、このようなクラスは、System.Collections.Generic.Queue<T> から宣言できます。
Public stringQ As New System.Collections.Generic.Queue(Of String)
このときに、stringQ を使って、String 値だけを扱うように指定できます。stringQ は、Object 値を汎用的に扱うのではなく String だけを扱うことを意味するので、遅延バインディングまたは型変換は行いません。その結果、実行時間が短縮され、ランタイム エラーが減少します。
ジェネリック型の使い方の詳細については、「方法: ジェネリック クラスを使用する (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 は型パラメーターです。これはプレースホルダーなので、このクラスを宣言するときには適切なデータ型で置き換えてください。コードの他の部分では、t をさまざまなデータ型で置き換えることにより、classHolder のさまざまなバージョンを宣言できます。このようにして宣言した 2 つのクラスを次の例に示します。
Public integerClass As New classHolder(Of Integer)
Friend stringClass As New classHolder(Of String)
このステートメントでは、構成されるクラスを宣言し、その中で型パラメーターを特定の型に置き換えています。この置き換えは、構成されるクラスのコード全体に反映されます。次の例は、integerClass の processNewItem プロシージャがどのようなコードになるかを示しています。
Public Sub processNewItem(ByVal newItem As Integer)
Dim tempItem As Integer
' Inserted code now processes an Integer item.
End Sub
この例より完全なコード例については、「方法: 複数のデータ型に同一の機能を提供できるクラスを定義する (Visual Basic)」を参照してください。
使用できるプログラミング要素
ジェネリックのクラス、構造体、インターフェイス、プロシージャ、およびデリゲートを宣言し、使用できます。.NET Framework では、よく使われるジェネリックな要素を表すジェネリックのクラス、構造体、およびインターフェイスが定義されています。System.Collections.Generic 名前空間には、ディクショナリ、リスト、キュー、およびスタックが用意されています。独自のジェネリックな要素を定義する前に、それに相当する要素が既に System.Collections.Generic に用意されていないかを確認してください。
プロシージャは型ではありませんが、ジェネリック プロシージャを宣言し、使用できます。「Visual Basic におけるジェネリック プロシージャ」を参照してください。
ジェネリック型の利点
ジェネリック型は、それぞれが特定のデータ型を操作する複数のプログラミング要素を宣言するための基礎となります。ジェネリック型の代わりになりうるものを以下に示します。
Object データ型を操作する単一の型。
型の型固有バージョンのセット。それぞれの型は、個別にコーディングされ、String、Integer、または customer などのユーザー定義型などの特定のデータ型を操作します。
ジェネリック型には、これらの代替手段にはない次の利点があります。
**タイプ セーフ。**ジェネリック型では、コンパイル時に型がチェックされます。一方、Object に基づく型はすべてのデータ型を受け入れるので、入力したデータ型が受け入れられる型かどうかをコードでチェックする必要があります。ジェネリック型を使うと、型の不一致は実行時ではなくコンパイル時に検出されます。
パフォーマンスジェネリック型は、それぞれが特定の 1 つのデータ型に特化されるので、データをボックス化したり、ボックス化を解除したりする必要がありません。Object に基づいて操作を実行する場合、入力したデータ型をボックス化して Object に変換したり、出力時にデータのボックス化を解除したりする必要があります。ボックス化とボックス化解除は、パフォーマンスを低下させます。
また、Object に基づく型は遅延バインディングです。つまり、この型のメンバーにアクセスするには、実行時に余分なコードが必要になります。これも、パフォーマンスを低下させます。
**コードの統合。**ジェネリック型のコードは、一度だけ定義します。1 つの型の一連の型固有バージョンでは、同じコードが各バージョンに複製されます。バージョンによって異なるのは、扱うデータ型だけです。ジェネリック型では、すべての型固有バージョンが元のジェネリック型から生成されます。
**コードの再利用。**特定のデータ型に依存しないコードは、ジェネリックである場合に、さまざまなデータ型で再利用できます。予期しなかったデータ型に再利用できることもあります。
**IDE サポート。**ジェネリック型から宣言した構成型を使用すると、コードの開発中に統合開発環境 (IDE: Integrated Development Environment) から提供されるサポートが増えます。たとえば、Intellisenseは、コンストラクターまたはメソッドの引数の型固有のオプションを指定できます。
**ジェネリックなアルゴリズム。**型に依存しない抽象アルゴリズムは、ジェネリック型にすることをお勧めします。たとえば、IComparable インターフェイスを使って項目を並べ替えるジェネリック プロシージャは、IComparable を実装する任意のデータ型に使用できます。
制約
ジェネリック型定義のコードはできる限り型に依存しない必要がありますが、なんらかのデータ型の機能がジェネリック型に必要な場合もあります。たとえば、並べ替えや照合順序のために 2 つの項目を比較する必要がある場合、それらのデータ型は IComparable インターフェイスを実装する必要があります。この要件を強制するには、制約を型パラメーターに追加します。
制約の例
次のコード例は、IComparable の実装を型引数に強制する制約があるクラスのスケルトン定義を示しています。
Public Class itemManager(Of t As IComparable)
' Insert code that defines class members.
End Class
後続のコードで、IComparable を実装しない型を渡して itemManager からクラスを作成しようとすると、コンパイラがエラーを生成します。
制約の種類
次の要件を任意に組み合わせて制約を指定できます。
型引数は、1 つまたは複数のインターフェイスを実装する。
型引数は、クラスの型そのものであるか、最大 1 つのクラスを継承する。
型引数はパラメーターなしのコンストラクターを公開し、そのコンストラクターからオブジェクトを作成するコードで使用できるようにする。
型引数は、常に参照型である。または、常に値型である。
複数の要件を指定する場合は、コンマで区切られた制約リストを中かっこ ({ }) で囲みます。アクセス可能なコンストラクターの作成を必須とするには、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
重要な用語
ジェネリック型に関連して、以下の新しい用語が使用されます。
ジェネリック型。宣言時に少なくとも 1 つのデータ型を指定するクラス、構造体、インターフェイス、プロシージャ、またはデリゲートの定義です。
型パラメーター。ジェネリック型定義で、データ型を宣言するときにデータ型の代わりに指定するプレースホルダーです。
型引数。構成型をジェネリック型から宣言するときに、型パラメーターを置き換える特定のデータ型です。
制約。型パラメーターに指定できる型引数を制限する条件です。型引数が特定のインターフェイスを実装すること、特定のクラスであるか特定のクラスを継承すること、アクセス可能なパラメーターなしのコンストラクターを持つこと、または参照型または値型であることを強制できます。このような制約は組み合わせて指定できますが、指定できるのは 1 つのクラスだけです。
構成型。型パラメーターに型引数を指定することにより、ジェネリック型から宣言されるクラス、構造体、インターフェイス、プロシージャ、またはデリゲートです。
参照
処理手順
データ型のトラブルシューティング (Visual Basic)