2009-08-25 8 views
3

は、私は以下のコードについていくつか質問している:構造体の場合は、いつ使用するステートメントボックスにその引数がありますか?

using System; 

namespace ConsoleApplication2 
{ 
    public struct Disposable : IDisposable 
    { 
     public void Dispose() { } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      using (Test()) { } 
     } 

     static Disposable Test() 
     { 
      return new Disposable(); 
     } 
    } 
} 

私の質問は以下のとおりです。

  • Disposable構造体上で動作し、ステートメントを使用しますが、Test()ボックスから構造体を返され、そうでありませんか?
  • 私は自分自身で答えを見つけることができますか?自分自身を見つけるためにしようとする

は、私は上記のコードによって産生されたILを検査し、ここでILはMain(...)方法のためです。私は、そこに仮想メソッドの呼び出しを疑う

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 1 
    .locals init (
     [0] valuetype ConsoleApplication2.Disposable CS$3$0000) 
    L_0000: call valuetype ConsoleApplication2.Disposable ConsoleApplication2.Program::Test() 
    L_0005: stloc.0 
    L_0006: leave.s L_0016 
    L_0008: ldloca.s CS$3$0000 
    L_000a: constrained ConsoleApplication2.Disposable 
    L_0010: callvirt instance void [mscorlib]System.IDisposable::Dispose() 
    L_0015: endfinally 
    L_0016: ret 
    .try L_0006 to L_0008 finally handler L_0008 to L_0016 
} 

L_0010はボクシング操作を導入しますが、実際のbox命令はここにはありません。

私が尋ねる理由は、おそらく約1〜2年前、私はオンラインで「誰かがコメントした」文の「最適化」を見ていたからです。このケースでは、メソッドでロックが取得されたオブジェクトの短時間ロックの構文としてusingステートメントが使用され、処分されたときにロックが解放されるstructが返された:

using (LockTheObject()) 
{ 
    // use the object 
} 

及びコメントが使用される実際の構造体にIDisposableからLockTheObjectメソッドの戻り型を変更することにより、ボクシングが回避されたことでした。

しかし、これが真実か、それとも真実かは疑問です。

誰でも正しい方向に向けることができますか?ボックス操作を見るために、ランタイムアセンブリコードを調べなければならない場合は、何を探すかの例を示してください。アセンブリコードに精通しているので問題はありませんが、何も飛び出さない私はそれを見て私にも。

答えて

6

usingステートメントに入れられる値の型はボックス化されないように見えます。これは、IDisposableを実装する値の型が他のコンテキストではなく、usingステートメントにある場合にのみボクシングが省略されるため、C#最適化のようです。詳細情報については

The Using Statement And Disposable Value Typesを参照してください。

しばらく前にイアン・グリフィス氏は、 構造体へのクラスから、それを変更している彼のTimedLockクラス 約 改善を書きました。この変更により、 IDisposableを実装する 値タイプが発生しました。私はすぐに忘れてしまった の時間の私の心の後ろにつまらない質問 を持っていた。 質問は、Disposeを呼び出すときに タイプget boxedのインスタンスではありませんか?

Oh No! Not the TimedLock Again!

ジョン・サンズは、私が C#のlockキーワードの利便性のほとんどを放棄 せずにロックのタイムアウトを使用して ため、最近のブログで示した コードの欠陥を指摘します。

+0

は、拘束されたとcallvirtの間の相互作用は、私は感謝、探していたものでした! –

3

参照型のインスタンスメソッドと同じように、値型のインスタンスメソッドは、最初の引数としてthisパラメーターをとります。ただし、この場合のパラメータは、ボックス化されたオブジェクトへの参照ではなく、オブジェクトのデータへの管理されたポインタです。あなたはそれがこのようにメモリにレイアウトを見つけるかもしれない:

Unboxed object: 
----------------------------------------- 
|    DATA      | 
----------------------------------------- 
^ managed pointer to struct 

Boxed object: 
------------------------------------------------------------ 
| GC/Object header |    [Boxed] DATA    | 
------------------------------------------------------------ 
        ^The 'unbox' opcode gives a managed pointer to the boxed data 
^ A *reference* to any instance of a reference type or boxed object, points here 

DATAは、これらのcases¹の両方で同じです。

値型のインスタンスメソッドは、オブジェクトへのボクシングのためにの具体的にはのデータへのマネージポインタを必要とします。上記のように、電話の前にconstrainedオペコードが使用されています。これは、次のcallvirt命令が通常受け取るオブジェクト参照の代わりにConsoleApplication2.Disposable構造体へのマネージドポインタを受け取っていることをランタイムに伝えます。そうすることで、JITは構造体によって実装されたDispose()の密封されたオーバーロードを解決し、オブジェクトをボクシングすることなく直接呼び出すことができます。 constrainedのプレフィックスがない場合、callvirt命令に渡されるオブジェクトはオブジェクト参照でなければなりません。標準の仮想呼び出し動的解決プロシージャは、GC /オブジェクトヘッダーが常に予想される場所にあるです。 、これは値の種類のボクシングを強制する。

1私たちは先に進み、今はNullable<T>を無視します。

4

これはIf my struct implements IDisposable will it be boxed when used in a using statement?

UPDATEの複製である:この質問はthe subject of my blog in March of 2011ました。素晴らしい質問をありがとう!

Andrew Hareの答えは正しいです。私は面白い余分なメモを追加したかっただけです。可能なときにボクシングをスキップするために制約付きコールバットを使用するという最適化は、厳密にはC#仕様の違反です。仕様では、値型リソースに対して生成するfinallyブロックは、値型のボクシング変換であることが明らかです。

 finally 
    { 
     ((IDisposable)resource).Dispose(); 
    } 

実装におけるボクシングの欠如が目に見えるような工夫されたシナリオを構築することは可能です。

(おかげで私には、このスペック違反を指摘するためにウラジミールReshetnikovに起因するものである。)

+0

私は時々、「ボクシングなしの再解釈」型キャストを指定するための構文がないことに驚いています。 'resource'は' IDisposable'制約を満たす型でなければならないので、仕様が 'static void DisposeIt (ref T it)を呼び出すのと同等であると言うことができるのだろうかと思うのですが、IDisposable {it。 C#コンパイラがインラインでコードを生成する可能性があることを除いて[Dispose();} 'を使用しています(C#コンパイラがインライン化していなくても、JITterかもしれません)。 – supercat

+0

こんにちはEric、私は制約付きのコールバットが今のところC#仕様の違反であるのだろうかと思います。 – codingadventures

関連する問題