4

このILフラグメント(MicrosoftのC#コンパイラによって生成されたもの)について考えてみます。

.class public sequential ansi sealed beforefieldinit Foo
       extends [mscorlib]System.ValueType
{ … }

.method private hidebysig static void  Main(string[] args) cil managed
{
  .maxstack  1
  .locals init ([0] valuetype Foo foo)

  ldloca.s          foo                                                   // ?
  constrained. Foo                                                        // ?
  callvirt          instance string [mscorlib]System.Object::ToString()   // ?
  pop
  ret
}

マークされた3行で何が起こっているのかを正確に知りたい// ?:仮想メソッド(System.Object's ToString)がボックス化されていない値型で呼び出される可能性はありますか(CLI仕様のセクションI.8.9.7による)基本型はまったくありませんか?

私の現在の不完全な理解はこれです:

  • ldloca.s foo結果として*、ローカル変数foo(タイプのボックス化されていない値を含む)への一時ポインター()が生成されますvaluetype Foo。この場合、CLI仕様のセクションI.12.3.2.1に従って、マネージポインター(&)が期待される場所で使用できます。

  • この*ポインターは、メソッド呼び出しのthisポインターとして機能します。ここではマネージポインタ()として機能できるため、これは合法であるように見え&ます。CLI標準では、セクションI.8.9.7でこの可能性について言及しています。

  • constrained. Fooプレフィックスはvaluetype Foo、オブジェクトへの値のボックス化を防ぐためにありboxed Fooます。

しかし、主な問題は残っています。仮想メソッドを継承しないボックス化されていない値で仮想メソッドを呼び出すことができるのはなぜですか。

4

1 に答える 1

4

System.Object.ToString(CLI仕様のセクションI.8.9.7によると)基本型がまったくない、ボックス化されていない値型で仮想メソッドが呼び出される可能性はありますか?

私はその質問に混乱しています。基本タイプの有無はそれと何の関係がありますか?

3行で何が起こっているのか正確に知りたい

キーはconstrainedプレフィックスです。ドキュメント(パーティションIIIセクション2.1)は非常に簡単です。ドキュメントには、レシーバーのタイプ、thisTypeそのタイプへのマネージポインターptr、およびのconstrained.callvirtがありmethodます。ルールは次のとおりです。

  1. thisTypeが参照型の場合、は逆参照され、のポインタptrとして渡されます。thiscallvirtmethod
  2. thisTypeが値型であり、thisType実装されている場合は、によって実装されたのへのポインタとして変更されずmethodptr渡されます。thiscallmethodthisType
  3. thisTypeが値型であり、thisTypeメソッドを実装していない 場合は、逆参照さptrれ、ボックス化され、のポインタとして渡されますthiscallvirtmethod

あなたの例では、ポイント(3)が当てはまります。タイプFooは値型であり、メソッドを実装しないためToString、ボックス化され、ボックスを参照してメソッド(基本クラスによって提供される)が呼び出されますthis

int.ToStringがあるとします。次に、ポイント(2)が適用されます。タイプはint、値タイプでありint、のオーバーライドを実装しますSystem.Object.ToString()。したがって、へのマネージポインタはへの呼び出しのになりintます。これにより、不要なボクシングが排除されます。(そして、変異した場合、変異は、ボックス化されたコピーではなく、レシーバーとして指定された変数で発生します。)thisToStringToStringint

その仮想メソッドを継承しないボックス化されていない値で仮想メソッドを呼び出すことができるのはなぜですか?

手元の問題は、上で引用したドキュメントで指摘されているように、メソッドが実装されているかどうかです。継承はそれと何の関係がありますか?

あなたが尋ねなかった質問ですが、これはそれに答えるのに良い場所です:

値型に常にToStringを実装する必要がありますか?

まあ、私はいつも知っているわけではありませんが、(1)のデフォルトの実装ToStringは陰気であり、(2)値型に実装することでボクシングのペナルティをなくすことができるので、そうすることは確かに良い考えですメソッドが直接呼び出されるときはいつでも。

同じことがオブジェクトの他の仮想メソッドにも当てはまりますか?

うん。そして、とにかく値型で独自の同等性とハッシュを作成するのには十分な理由があります。デフォルト値の型の同等性は、予期しない場合があります。

GetTypeは仮想ではないことに注意してください。それは関係がありますか?

はい; 仮想ではないということは、値型でオーバーライドできないことを意味します。つまりGetType、任意の値型を呼び出すと、常にそれがボックス化されます。もちろん、ボックス化されていない値型が手元にある場合は、コンパイル時にその型が何であるかをすでに知っているので、呼び出す必要はありません。GetType

于 2013-02-22T22:26:03.700 に答える