7

クラスの答えはわかっていると思いますが、自分の理解が正しいことを確認したいだけです。ClassAという名前の とそのインスタンスがあるとしますaa.MethodA()が呼び出されるとき:

(1) CLRClassA型ポインターからaヒープ内の型を見つける (型はヒープに読み込まれている)

(2)MethodA型を検索し、見つからない場合は、その基本型に移動し、objectクラスまで。

私の理解は正確ではないかもしれませんが、基本的には正しいと思います (間違っていたら訂正してください!)。ここで、単純なstructの問題が発生します。

struct MyStruct
{
   public void MethodA() { }
}

がありvar x = new MyStruct();、その値はスタックにあり、の型はMyStructヒープにロードされています。executex.MethodA()の場合、もちろんボクシングはありません。CLR はどのようMethodAに IL を見つけて取得し、それを実行/JIT しますか? 答えはおそらく次のようになると思います:(繰り返しますが、間違っていたら訂正してください)

(1) スタック上に の宣言型xあります。CLR は、スタック上の情報からその型を見つけMethodA、その型を見つけます。-- と呼びましょうassumptionA

assumptionAが正しいと言っていただければ幸いです。しかし、それが間違っていても、それは真実を語っています。CLR には、ボクシングせずに構造体の型を見つける方法があります。

では、x.ToString()またははx.GetType()どうでしょうか。値がボックス化され、クラスのように機能することがわかっています。しかし、なぜここでボクシングが必要なのでしょうか? その型を取得できるので(仮定Aが教えてくれます)、その基本型に移動して(クラスのように)メソッドを見つけてみませんか?なぜここで高価なボックス操作が必要なのですか?

4

3 に答える 3

5

仮定Aは間違っています。C# コンパイラのシンボル テーブルには、型情報が格納されます。その静的型情報はほとんどすべての場合に使用されます。オブジェクトに格納された動的型は、型チェック (is演算子)、キャスト (as演算子と実際のキャスト構文)、および配列の分散中にのみ必要であり、動的型がそうでない場合にのみ必要です。 t はコンパイラに認識されません。ボックス化されていない構造体の動的型は常に静的に認識され、クラス インスタンスの動的型は、インスタンス化の近くおよび型チェックを実行した条件付きブロック内で静的に認識されます (たとえばif (x is T) y = (T)x;、型は then 部分内で認識されるため、キャストは別の動的チェックを必要としません)。

さて、C# コンパイラは の型を静的に認識しているため、オーバーロードの解決を行い、呼び出されている正確なMethodAxを見つけることができます。次に、MSIL を発行して引数を MSIL 仮想スタックにプッシュし、その特定のメソッドへのメタデータ参照を含む呼び出し命令を発行します。実行時に型チェックは必要ありません。

の場合x.ToString()、C# コンパイラは呼び出したい正確なメソッドを認識しています。ToStringが型によってオーバーライドされている場合はstruct、型pointer-to-MyStructのパラメーターが必要です。これは、コンパイラーがボックス化せずに処理します。がオーバーライドされていない場合ToString、コンパイラは への呼び出しを生成します。この呼び出しObject.ToStringは、オブジェクトをそのパラメーターとして期待します。x正しいタイプとして MSIL 仮想スタックをプッシュするには、ボックス化が必要です。

GetType型が静的に認識されている特殊なケースです。コンパイラはメソッドを呼び出さず、シンボル テーブルから型情報を取得し、メタデータ参照を直接 MSIL に挿入します。

于 2011-03-31T04:48:06.170 に答える
4

さて、ここではいくつかの異なることが起こっています。

  • struct で定義されているメソッドの場合、CLR はアセンブリ メタデータの型定義を調べて、メソッドが何であるかを判断します。メソッドが をFoo呼び出すMethodAと、CLR は正しいものにバインドします。メソッドの場合MethodAは JIT されます。コンパイルがすべて行われた後、実際には何も起こりません。必要な情報がすでに存在するため、メソッドは直接呼び出されます。

  • のような仮想 継承された構造体メソッドの場合ToString、設計上、仮想呼び出しは s でのみ呼び出すことできるため、ボックス化が必要です。ボックスObject化がないと、結果のメソッドを把握するために調べる v-table がありません。(メソッド呼び出しがボクシングの直後に行われる可能性があるという事実は、潜在的な最適化を可能にする可能性がありますが、それはロング ショットです。JIT コンパイラーがこれを行うとは思えません。)どうやらボクシングはありません。これらのメソッドがオーバーライドされていることに気付かなかったので、私は間違っていました。実際、オーバーライドされたメソッドの場合、コンパイラメソッドを直接呼び出すだけで最適化を実行します。そうしない理由がないからです。(ありませんオーバーライドされない値型の仮想メソッドなので、実際にはここでは問題になりません。)

  • inheritedである非仮想構造体メソッドの場合、メソッドが定義上、値型ではなく参照型で呼び出されるため、オブジェクトをボックス化する必要があります。コンパイラでこれを特別なケースにする必要はありません.JITコンパイラは、次のようなメソッドをJITしているときに実際に最適化(ボクシングの回避など)を実行できると信じているためです(ただし、この最適化について間違っている場合は誰かが私を修正してください)。GetType

于 2011-03-31T02:53:13.850 に答える
0

編集: コメントありがとうございます。私はそれがどのように機能するかを理解していると思っていました... もうそうではありません。したがって、これを調査の出発点として残しますが、答えではありません。

v-table ルックアップの必要がないため、構造体で ToString またはその他の仮想関数を呼び出すためのボックス化が発生する可能性があります。構造体は封印されているため、正確なメソッドがわかっているため、コンパイル時間が短縮されます。

一方、コメントで指摘されているように、基本クラスの仮想関数には「this」パラメーターとしてオブジェクトが必要です。

生成された IL を 3 番目に見ると、ToString と GetHashCode が実際にボクシングを行うかどうかは不明です (これらの場合のボクシングに関するコメントがここにあるため、どこかに隠されている可能性がありますhttp://blogs.msdn.com/b/lucabol/archive/2007 /12/24/creating-an-immutable-value-object-in-c-part-iii-using-a-struct.aspx )。GetType には明示的なボクシングが必要です。

ILDasm の出力を調べて、ボクシングまたは直接呼び出しがあるかどうかを確認します。

       int v = 42;

        string s = v.ToString();

        object a = v;
        s = a.ToString();

次の IL にコンパイル (デバッグ) します。int.ToString() のボクシングはありませんが、オブジェクトへのキャストは間違いなく 1 つです...

  IL_0001:  ldc.i4.s   42
  IL_0003:  stloc.0
  IL_0004:  ldloca.s   v
  IL_0006:  call       instance string [mscorlib]System.Int32::ToString()
  IL_000b:  stloc.1
  IL_0013:  ldloc.0
  IL_0014:  box        [mscorlib]System.Int32
  IL_0019:  stloc.2
  IL_001a:  ldloc.2
  IL_001b:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0020:  stloc.1
于 2011-03-31T04:35:53.733 に答える