ランタイムのジェネリック (C# プログラミング ガイド)

更新 : 2007 年 11 月

ジェネリック型またはジェネリック メソッドを Microsoft Intermediate Language (MSIL) にコンパイルすると、型パラメータを持つと識別されるメタデータが組み込まれます。ジェネリック型の MSIL の使用方法は、指定した型パラメータが値型か参照型かによって異なります。

パラメータとして値型を持つジェネリック型を初めて構築すると、ランタイムは指定したパラメータが適切な位置で置換された特殊なジェネリック型を MSIL で作成します。特殊なジェネリック型は、パラメータとして使用される一意の値型ごとに、1 回作成されます。

たとえば、プログラミング コードで、次のように整数で構築されたスタックが宣言されているとします。

Stack<int> stack;

この時点で、ランタイムはパラメータを整数に置き換えた特殊なバージョンの Stack<T> クラスを生成します。これで、プログラミング コードで整数のスタックを使用すると、ランタイムでは、この生成された特殊な Stack<T> クラスが必ず再利用されるようになります。次の例では、整数スタックの 2 つのインスタンスを作成し、これらのインスタンスが Stack<int> コードの単一のインスタンスを共有します。

Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();

一方、long などの異なる値型やユーザー定義の構造体をパラメータとして持つ別の Stack<T> クラスがコードの別の箇所で作成されるとします。その結果、ランタイムは別のバージョンのジェネリック型を生成し、MSIL で適切な位置にある long を置換します。この特殊なジェネリック クラスにはそれぞれネイティブに値型が含まれるため、変換は必要ありません。

参照型の場合、ジェネリックの機能はやや異なります。ジェネリック型を参照型で初めて構築すると、MSIL のパラメータを置換するオブジェクト参照を持つ、特殊なジェネリック型がランタイムで作成されます。次に、構築された型がパラメータとして参照型でインスタンス化されるたびに、その型の種類にかかわらず、以前に作成した特殊なバージョンのジェネリック型がランタイムで再利用されます。参照はいずれも同じサイズのため、このように処理されます。

たとえば、Customer クラスと Order クラスという 2 つの参照型があり、さらに Customer 型のスタックを作成したとします。

class Customer { }
class Order { }
Stack<Customer> customers;

この時点で、データではなく、後で入力されるオブジェクト参照を格納した、特殊なバージョンの Stack<T> クラスがランタイムで生成されます。次のコード行で、Order という別の参照型のスタックが作成されるとします。

Stack<Order> orders = new Stack<Order>();

値型とは異なり、Order 型用に特殊なバージョンの Stack<T> クラスは新たに作成されません。代わりに、特殊なバージョンの Stack<T> クラスのインスタンスが作成され、orders 変数はそのインスタンスを参照するように設定されます。このとき、Customer 型のスタックを作成するコード行があるとします。

customers = new Stack<Customer>();

前述した、Order 型を使用して作成された Stack<T> クラスを使用する場合と同様に、特殊な Stack<T> クラスのインスタンスが新たに作成されます。インスタンスに含まれるポインタは、Customer 型のサイズを示すメモリ領域を参照するように設定されます。参照型の数はプログラムによって大幅に異なるため、C# のジェネリックの実装は、参照型のジェネリック クラスのためにコンパイラで作成される特殊なクラスの数を 1 つに減らし、コードの量を大幅に少なくします。

さらに、ジェネリック C# クラスを値型パラメータまたは参照型パラメータを使用してインスタンス化する場合は、それに対してリフレクションが実行時にクエリを実行でき、実際の型と型パラメータの両方を確認できます。

参照

概念

C# プログラミング ガイド

参照

ジェネリックの概要 (C# プログラミング ガイド)

System.Collections.Generic

その他の技術情報

.NET Framework におけるジェネリック