1. ホーム
  2. c#

[解決済み] 再定義とは何ですか?

2022-04-22 18:43:43

質問

Javaは消去法でパラメトリックポリモーフィズム(Generics)を実装していることは知っています。消去が何であるかは理解しています。

C#がreificationでパラメトリックポリモーフィズムを実装していることは知っている。と書かせることができることを知っています。

public void dosomething(List<String> input) {}
public void dosomething(List<Int> input) {}

とか、パラメタライズされたある型の型パラメータが何であるかを実行時に知ることができるとか、よくわからないのですが .

  • 再定義された型とは何ですか?
  • Reified Valueとは何ですか?
  • 型/値が再定義されるとどうなりますか?

どのように解決するのですか?

再定義とは、抽象的なものを具体的なものに作り変えることです。

この用語は レフィケーション C#のジェネリックスでは 汎用型定義 と1つ以上の 汎用型引数 (抽象的なもの)が組み合わされ、新しい 汎用型 (具体的なもの)です。

言い方を変えると、定義をもとに List<T>int を生成し、具体的な List<int> という型があります。

さらに理解するために、次のようなアプローチを比較してみてください。

  • Javaジェネリックスでは、ジェネリック型の定義は、基本的に、許可されたすべての型引数の組み合わせで共有される1つの具体的なジェネリック型に変換されます。したがって、複数の(ソースコードレベルの)型が1つの(バイナリレベルの)型にマッピングされます - ただし、結果として インスタンスの型引数に関する情報は、そのインスタンスでは破棄されます(型消去)。 .

    1. この実装手法の副作用として、ネイティブに許可される汎用型引数は、その具象型のバイナリコードを共有できる型のみです。これは、記憶場所が交換可能な表現を持つ型、つまり参照型を意味します。 汎用型引数として値型を使用するには、値型を箱詰めする必要があります。 (単純な参照型ラッパーに入れる)。
    2. この方法でジェネリックスを実装するために、コードが重複することはありません。
    3. 実行時に(リフレクションを使用して)利用できたはずの型情報が失われる。これは逆に、ジェネリックタイプの特殊化(特殊化された ソースコード は、特定のジェネリック引数の組み合わせに対して非常に制限されます。
    4. この機構は、実行環境からのサポートを必要としない。
    5. いくつかの 型情報を保持するための回避策 JavaプログラムまたはJVMベースの言語が使用することができます。
  • C#のジェネリックでは、ジェネリックの型定義は実行時にメモリ上に保持されます。新しい具象型が必要になると、実行時環境は汎用型定義と型引数を組み合わせて新しい型を作成する(再定義)。つまり、型引数の組み合わせごとに新しい型が得られるわけです。 実行時 .

    1. この実装手法により、どのような型引数の組み合わせでもインスタンス化することができます。汎用型引数として値型を使用しても、これらの型はそれ自身の実装を得るので、箱詰めは起こりません。( C#ではまだボクシングが存在する もちろん、これは他のシナリオで起こることで、このシナリオではありません)。
    2. コードの重複は問題になりえますが、実際にはそうではありません。なぜなら、十分に賢い実装( これにはMicrosoft .NET モノ ) は、いくつかのインスタンス化についてコードを共有することができます。
    3. 型情報を保持しているため、リフレクションで型引数を調べることである程度特殊化できる。しかし、一般的な型定義がコンパイルされる結果、特殊化の程度は制限される。 前に 再定義が行われます(これは 型パラメータの制約に対して定義をコンパイルします。 - このように コンパイラは、特定の型引数がない場合でも、定義を理解できなければなりません。 ).
    4. この実装手法は、ランタイムサポートとJITコンパイルに大きく依存します(そのため、しばしば C#のジェネリックは、iOSのようなプラットフォームではいくつかの制限があります。 動的なコード生成が制限されているため)。
    5. C#のジェネリックの文脈では、再定義は実行環境によって行われます。しかし、ジェネリック型の定義と具象ジェネリック型の違いをより直感的に理解したい場合。 を使えば、いつでも自分で再定義を行うことができます。 System.Type クラス (インスタンス化する特定の汎用型引数の組み合わせがソースコードに直接現れなかったとしても)。
  • C++のテンプレートでは、テンプレート定義はコンパイル時にメモリ上に保持されます。ソースコードの中でテンプレート型の新しいインスタンス化が必要になると、コンパイラはテンプレート定義とテンプレート引数を組み合わせて新しい型を生成します。つまり、テンプレート引数の組み合わせごとに一意な型が得られるわけです。 コンパイル時 .

    1. この実装手法により、どのような型引数の組み合わせでもインスタンス化することができます。
    2. これはバイナリコードを重複させることが知られていますが、十分に賢いツールチェーンはこれを検知し、いくつかのインスタンス化のためのコードを共有することができます。
    3. テンプレート定義そのものはコンパイルされません。 その具体的なインスタンスのみが実際にコンパイルされます。 . これにより、コンパイラの制約が少なくなり、より高度な操作が可能になります。 テンプレート特殊化 .
    4. テンプレートのインスタンス化はコンパイル時に行われるため、ここでも実行時のサポートは必要ありません。
    5. このプロセスは、最近、次のように呼ばれています。 単形化 特にRustコミュニティにおいて。この言葉は、以下のものと対比して使われます。 パラメトリックポリモーフィズム これは、ジェネリックの由来となる概念の名前です。