ジェネリックはどのように実装されますか?


16

これは、コンパイラ内部の観点からの質問です。

テンプレート(C ++)ではなくジェネリックに興味があるため、質問にC#を付けました。なぜなら、両言語のジェネリックは実装が異なるためです。

ジェネリックなしの言語を見ると、非常に簡単です。クラス定義を検証し、階層に追加するだけです。

しかし、ジェネリッククラスをどうするか、さらに重要なことには、それへの参照をどのように処理するのでしょうか?インスタンス化ごとに静的フィールドが特異であることを確認する方法(つまり、ジェネリックパラメーターが解決されるたびに)。

私は電話を見たとしましょう:

var x = new Foo<Bar>();

Foo_Bar階層に新しいクラスを追加しますか?


更新:これまでのところ、関連する投稿は2つしか見つかりませんでしたが、「自分でどうやってやるのか」という意味ではあまり詳しくありません。


完全な答えが面白いと思うので、賛成です。私はそれがどのように機能するかについていくつかのアイデアを持っていますが、正確に答えるには十分ではありません。C#のジェネリックは、各ジェネリック型の専用クラスにコンパイルされるとは思わない。これらは実行時に解決されるようです(ジェネリックを使用すると、著しい速度低下が発生する可能性があります)。たぶん、エリック・リッパートに声をかけてもらうことができますか?
-KChaloux

2
@KChaloux:MSILレベルでは、ジェネリックの説明が1つあります。JITが実行されると、汎用パラメーターとして使用される値タイプごとに個別のマシンコードが作成され、すべての参照タイプをカバーするマシンコードのセットがもう1つ作成されます。MSILで一般的な説明を保存すると、実行時に新しいインスタンスを作成できるため、非常に便利です。
ベンフォークト

@ベンそれは私が実際に質問に答えようとしなかった理由です:p
KChaloux

あなたがまだ周りにいるかどうかはわかりませんが、どの言語にコンパイルていますか。これは、ジェネリックの実装方法に大きな影響を与えます。通常、フロントエンドでどのようにアプローチしたかについての情報を提供できますが、バックエンドは大きく異なる場合があります。
テラスティン

@Telastyn、これらのトピックのために私は確かです:-)私はC#に本当に近いものを探しています、私の場合はPHP にコンパイルしています(冗談はありません)。知識を共有していただければ幸いです。
greenoldman

回答:


4

インスタンス化ごとに静的フィールドが特異であることを確認する方法(つまり、ジェネリックパラメーターが解決されるたびに)。

各汎用インスタンス化には、(紛らわしい名前の)MethodTableのコピーがあり、静的フィールドが格納されます。

私は電話を見たとしましょう:

var x = new Foo<Bar>();

Foo_Bar階層に新しいクラスを追加しますか?

クラス階層を実行時に実際に存在する何らかの構造と考えるのが有用かどうかはわかりませんが、それはより論理的な構造です。

しかし、それぞれがその基本クラスへの間接ポインターを持つMethodTableを考慮してこの階層を形成する場合、そう、これにより新しいクラスが階層に追加されます。


ありがとう、それは面白い作品です。静的フィールドは仮想テーブルと同様に解決されますよね?タイプごとにエントリを保持する「グローバル」辞書への参照はありますか?だから、使用Foo<string>してお互いを知らない2つのアセンブリを持つことができ、静的フィールドの2つのインスタンスを生成しませんFoo
greenoldman

1
@greenoldmanさて、仮想テーブルとは異なり、まったく同じです。MethodTableは、静的フィールドと、仮想ディスパッチで使用される型のメソッドへの参照の両方を保持します(そのため、MethodTableと呼ばれます)。そして、そうです、CLRには、すべてのMethodTablesにアクセスするために使用できるテーブルが必要です。
svick

2

そこには2つの実際の具体的な質問があります。完全な理解を得るために、おそらく追加の関連する質問(この質問に戻るリンクを持つ別の質問として)を聞きたいと思うでしょう。

静的フィールドには、汎用インスタンスごとに個別のインスタンスが与えられますか?

まあ、ジェネリック型パラメーターに関連しない静的メンバーの場合、これは非常に簡単です(ジェネリックパラメーターから値にマップされた辞書を使用します)。

型パラメーターに関連するメンバー(静的または非静的)は、型消去によって処理できます。最も強い制約(多くの場合System.Object)を使用します。型情報はコンパイラの型チェック後に消去されるため、実行時の型チェックは不要であることを意味します(ただし、実行時にインターフェイスキャストが存在する場合があります)。

各汎用インスタンスは、タイプ階層に個別に表示されますか?

.NETジェネリックではありません。型パラメーターからの継承を除外することが決定されたため、ジェネリックのすべてのインスタンスが型階層の同じ場所を占めることがわかりました。

基本クラスから名前を検索できなかったのは驚くほど驚くべきことだったので、これはおそらく良い決断でした。


私の問題は、テンプレートの観点から考えることから逃れることができないということです。たとえば、テンプレートのジェネリッククラスとは異なり、完全にコンパイルされます。これは、このクラスを使用する他のアセンブリで何が起こるかを意味しますか?既にコンパイルされたメソッドは、内部キャストで呼び出されますか?私はジェネリックが制約に頼ることができるFoo<int>Foo<string>は思わない-そうではなく、引数に依存し、制約なしで同じデータをヒットするだろうFoo
greenoldman

1
@greenoldman:値型は実際には特別に処理されるため、1分間は避けることができますか?List<string>and を持っている場合List<Form>List<T>内部にtypeのメンバーT[]があり、制約はないためT、実際に取得するのは、を操作するマシンコードですobject[]。ただし、Tインスタンスは配列に配置されるだけなのでT、追加の型チェックなしで、すべてが返される可能性があります。一方、を持っている場合ControlCollection<T> where T : Control、内部配列T[]はになりControl[]ます。
ベンフォークト

制約が内部型名として使用され、使用されることを正しく理解していますか?クラスが実際に使用される場合、キャストが使用されますか?OK、私はそのモデルを理解していますが、私はJavaがC#ではなくそれを使用するという印象を受けていました。
greenoldman

3
@greenoldman:Javaは、ソース->バイトコード変換ステップで型の消去を実行します。これにより、検証者は汎用コードを検証できなくなります。C#は、バイトコード->マシンコードのステップでそれを行います。
ベンフォークト

@BenVoigtジェネリック型に関する情報はJavaに保持されます。そうしないと、ソースなしではジェネリックを使用するクラスに対してコンパイルできません。AIUIのバイトコードシーケンス自体ではなく、クラスメタデータに保持されます。
ドナルドフェローズ

1

しかし、ジェネリッククラスで何をすべきか、さらに重要なことには、それへの参照をどのように処理するのでしょうか?

コンパイラのフロントエンドでの一般的な方法は、ジェネリック型(List<T>)とバインドされたジェネリック型(List<Foo>)の2種類のタイプインスタンスを持つことです。ジェネリック型は、どの関数が存在するか、どのフィールドを使用するかを定義し、どこTで使用されてもジェネリック型参照を持ちます。バインドされたジェネリック型には、ジェネリック型への参照と一連の型引数が含まれます。これは、具体的な型を生成するのに十分な情報を持っているので、汎用型参照をFoo型引数と置き換えたり、型引数を何であれ置き換えます。あなたは型推論と推論する必要がやっているときの区別のこの種のは重要であるList<T>対をList<Foo>

テンプレート(さまざまな実装を直接構築する)のようなジェネリックを考える代わりに、関数型のコンストラクター(ジェネリック引数が型を与える関数への引数のようなもの)のように考えると便利です。

バックエンドに関しては、私は本当に知りません。ジェネリックに関する私の仕事はすべて、バックエンドとしてCILをターゲットにしているので、そこでサポートされているジェネリックにコンパイルできます。


どうもありがとうございました(残念ながら私は掛け算の答えを受け入れることができません)。私がそのステップをほぼ正しく行ったことを聞くのは素晴らしいことです-私の場合List<T>、実際のタイプ(その定義)を保持していList<Foo>ますが、(用語の部分にも感謝します)私のアプローチではList<T>(もちろん今Fooの代わりにT)。
greenoldman
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.