Visual Basic におけるジェネリック型 (Visual Basic)

ジェネリック型 はさまざまなデータ型に対して同じ機能を実行するために必要な処理を行う、1 つのプログラミング要素です。 ジェネリック クラスまたはジェネリック プロシージャを定義すると、同じ機能を実行させる各データ型に対して、その機能を別々に定義する必要がありません。

これは、ヘッドの部分が交換可能な、ねじ回しのセットにたとえることができます。 回すねじを調べて、そのねじに合った正しいヘッド (マイナス、プラス、星型) を選択します。 ねじ回しのハンドルに正しいヘッドを挿入したら、ねじ回しを使ってまったく同じ作業 (ねじを回すこと) を行います。

Diagram of a screwdriver set with different heads.

ジェネリック型を定義する場合は、1 つ以上のデータ型でジェネリック型をパラメーター化します。 これにより、ジェネリック型を使用するコードで、データ型をコードの要件に合わせて変更できるようになります。 コードでは、1 つのジェネリックな要素から複数のプログラミング要素を宣言し、それぞれを異なるデータ型のセットに使用できます。 ただし、使用するデータ型が異なっていても、宣言した要素はどれも同じロジックを実行します。

たとえば、 Stringなどの特定のデータ型を操作するキュー クラスを作成し、使用する必要があるとします。 次の例に示すように、このようなクラスは、 System.Collections.Generic.Queue<T>から宣言できます。

Public stringQ As New System.Collections.Generic.Queue(Of String)

このときに、 stringQ を使って、 String 値だけを扱うように指定できます。 stringQ は、 String 値を汎用的に扱うのではなく Object だけを扱うことを意味するので、遅延バインディングまたは型変換は行いません。 その結果、実行時間が短縮され、ランタイム エラーが減少します。

ジェネリック型の使い方の詳細については、「方法:ジェネリック クラスを使用する」をご覧ください。

ジェネリック クラスの例

次の例は、ジェネリック クラスのスケルトン定義を示しています。

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型パラメーターです。これはプレースホルダーなので、このクラスを宣言するときにはデータ型で置き換えます。 コードの他の部分では、 classHolder をさまざまなデータ型で置き換えることにより、 tのさまざまなバージョンを宣言できます。 このようにして宣言した 2 つのクラスを次の例に示します。

Public integerClass As New classHolder(Of Integer)
Friend stringClass As New classHolder(Of String)

このステートメントでは、 構成されるクラスを宣言し、その中で型パラメーターを特定の型に置き換えています。 この置き換えは、構成されるクラスのコード全体に反映されます。 次の例は、 processNewItemintegerClassプロシージャがどのようなコードになるかを示しています。

Public Sub processNewItem(ByVal newItem As Integer)
    Dim tempItem As Integer
    ' Inserted code now processes an Integer item.
End Sub

より完全なコード例については、「方法:複数のデータ型に同一の機能を提供できるクラスを定義する」をご覧ください。

使用できるプログラミング要素

ジェネリック クラス、構造体、インターフェイス、プロシージャ、およびデリゲートを定義して使用することができます。 .NET Framework では、よく使われるジェネリックな要素を表すジェネリックのクラス、構造体、インターフェイスが定義されています。 System.Collections.Generic 名前空間には、ディクショナリ、リスト、キュー、スタックが用意されています。 独自のジェネリックな要素を定義する前に、それに相当する要素が既に System.Collections.Genericに用意されていないかをご確認ください。

プロシージャは型ではありませんが、ジェネリック プロシージャを定義し、使用できます。 「 Generic Procedures in Visual Basic」を参照してください。

ジェネリック型の利点

ジェネリック型は、それぞれが特定のデータ型を操作する複数のプログラミング要素を宣言するための基礎となります。 ジェネリック型の代わりになるものを以下に示します。

  1. Object データ型を操作する単一の型。

  2. 型の 型固有 バージョンのセット。それぞれのバージョンは、個別にコーディングされ、 StringInteger、または customerなどのユーザー定義型などの特定のデータ型を操作します。

ジェネリック型には、これらの代替手段にはない次の利点があります。

  • タイプ セーフ。 ジェネリック型では、コンパイル時に型がチェックされます。 一方、 Object に基づく型はすべてのデータ型を受け入れるので、入力したデータ型が受け入れられる型かどうかをチェックするコードを記述する必要があります。 ジェネリック型を使うと、型の不一致は実行する前にコンパイラで検出できます。

  • パフォーマンス。 それぞれが特定の 1 つのデータ型に特化されるので、データを ボックス化 したり、 ボックス化を解除 したりする必要がありません。 Object に基づいて操作を実行する場合、入力したデータ型をボックス化して Object に変換したり、出力時にデータのボックス化を解除したりする必要があります。 ボックス化とボックス化解除は、パフォーマンスを低下させます。

    また、 Object に基づく型は遅延バインディングでもあります。つまり、この型のメンバーにアクセスするには、実行時に余分なコードが必要になります。 これも、パフォーマンスを低下させます。

  • コードの統合。 ジェネリック型のコードの定義は、一度だけ行う必要があります。 1 つの型の一連の型固有バージョンでは、同じコードが各バージョンに複製されます。バージョンによって異なるのは、扱うデータ型だけです。 ジェネリック型では、すべての型固有バージョンが元のジェネリック型から生成されます。

  • コードの再利用。 特定のデータ型に依存しないコードは、ジェネリックである場合に、さまざまなデータ型で再利用できます。 予期しなかったデータ型に再利用できることもあります。

  • IDE サポート。 ジェネリック型から宣言した構成型を使用すると、コードの開発中に統合開発環境 (IDE) から提供されるサポートが増えます。 たとえば、IntelliSense は、コンストラクターまたはメソッドに対して引数の型固有のオプションを示します。

  • ジェネリックなアルゴリズム。 型に依存しない抽象アルゴリズムは、ジェネリック型にすることをお勧めします。 たとえば、 IComparable インターフェイスを使って項目を並べ替えるジェネリック プロシージャは、 IComparableを実装する任意のデータ型に使用できます。

制約

ジェネリック型定義のコードはできる限り型に依存しない必要がありますが、なんらかのデータ型の機能がジェネリック型に必要な場合もあります。 たとえば、並べ替えや照合順序のために 2 つの項目を比較する必要がある場合、それらのデータ型は IComparable インターフェイスを実装する必要があります。 この要件を強制するには、 制約 を型パラメーターに追加します。

制約の例

次の例は、 IComparableの実装を型引数に強制する制約があるクラスのスケルトン定義を示しています。

Public Class itemManager(Of t As IComparable)
    ' Insert code that defines class members.
End Class

後続のコードで、 itemManager を実装しない型を渡して IComparableからクラスを作成しようとすると、コンパイラがエラーを生成します。

制約の種類

次の要件を任意に組み合わせて制約を指定できます。

  • 型引数は、1 つまたは複数のインターフェイスを実装する必要があります

  • 型引数は、クラスの型そのものであるか、最大 1 つのクラスを継承する必要があります

  • 型引数はパラメーターなしのコンストラクターを公開し、そのコンストラクターからオブジェクトを作成するコードで使用できる必要があります

  • 型引数は、 参照型である、または 値型である必要があります

複数の要件を指定する場合は、コンマで区切られた 制約リスト を中かっこ ({ }) で囲みます。 アクセス可能なコンストラクターを必須とするには、New 演算子のキーワードをリストに追加します。 参照型であることを必須とするには、 Class キーワードを追加し、値型であることを必須とするには、 Structure キーワードを追加します。

制約の詳細については、「 Type List」をご覧ください。

複数の制約の例

次の例は、型パラメーターに制約リストがあるジェネリック クラスのスケルトン定義を示しています。 このクラスのインスタンスを作成するコードでは、型引数が IComparable インターフェイスと IDisposable インターフェイスの両方を実装し、参照型であり、アクセス可能なパラメーターなしのコンストラクターを公開する必要があります。

Public Class thisClass(Of t As {IComparable, IDisposable, Class, New})
    ' Insert code that defines class members.
End Class

重要な用語

ジェネリック型に関連して、以下の新しい用語が使用されます。

  • ジェネリック型。 宣言時に少なくとも 1 つのデータ型を指定するクラス、構造体、インターフェイス、プロシージャ、またはデリゲートの定義です。

  • 型パラメーター。 ジェネリック型定義で、型を宣言するときにデータ型の代わりに指定するプレースホルダーです。

  • 型引数。 構成型をジェネリック型から宣言するときに、型パラメーターを置き換える特定のデータ型です。

  • 制約。 型パラメーターに指定できる型引数を制限する条件です。 型引数が特定のインターフェイスを実装すること、特定のクラスであるか特定のクラスを継承すること、アクセス可能なパラメーターなしのコンストラクターを持つこと、または参照型または値型であることを強制できます。 このような制約は組み合わせて指定できますが、指定できるのは 1 つのクラスだけです。

  • 構成型。 型パラメーターに型引数を指定してジェネリック型から宣言されるクラス、構造体、インターフェイス、プロシージャ、またはデリゲートです。

関連項目