9

私はこのMSDNMagazineの記事で、著者は次のように述べています(私の強調):

ボックス化は常に新しいオブジェクトを作成し、ボックス化されていない値のビットをオブジェクトにコピーすることに注意してください。一方、ボックス化を解除すると、ボックス化されたオブジェクト内のデータへのポインタが返されます。メモリコピーは発生しません。ただし、通常、コードによって、ボックス化されていない参照が指すデータがコピーされます。

太字にした文とそれに続く文に戸惑います。このMSDNページを含め、私が読んだ他のすべてのことから、ボックス化解除がヒープ上の値へのポインターを返すだけだと聞いたことはありません。開梱すると、最初と同じように、スタック上の値のコピーを含む変数が作成されるという印象を受けました。結局のところ、変数に「ヒープ上の値へのポインター」が含まれている場合は、値の型がなく、ポインターがあります。

誰かがこれが何を意味するのか説明できますか?作者はひび割れていましたか?(記事には他に少なくとも1つの明白なエラーがあります)。そして、これが真実である場合、「コードによって、ボックス化されていない参照が指すデータがとにかくコピーされる」場合はどうなりますか?

この記事は10年近く前のものであることに気づきました。おそらく、これは.Netの初期の段階で変更されたものかもしれません。

4

3 に答える 3

7

記事は正確です。ただし、コンパイラが生成するILの外観ではなく、実際に何が起こっているかについて説明します。結局のところ、.NETプログラムはILを実行することはなく、JITコンパイラによってILから生成されたマシンコードを実行します。

そして、unbox opcodeは実際に、値型の値を表すヒープ上のビットへのポインターを生成するコードを生成します。JITは、「JIT_Unbox」という名前のCLR内の小さなヘルパー関数への呼び出しを生成します。SSCLI20ソースコードを入手した場合は、clr \ src \ vm\jithelpers.cpp。Object :: GetData()関数はポインタを返します。

そこから、最も一般的には最初に値がCPUレジスタにコピーされます。その後、どこかに保存される可能性があります。スタックである必要はなく、参照型オブジェクト(gcヒープ)のメンバーである可能性があります。または静的変数(ローダーヒープ)。または、スタックにプッシュすることもできます(メソッド呼び出し)。または、値が式で使用される場合、CPUレジスタをそのまま使用することもできます。

デバッグ中に、エディターウィンドウを右クリックし、[逆アセンブリに移動]を選択して、マシンコードを表示します。

于 2010-06-05T06:01:11.687 に答える
5

元の記事の著者は、ILレベルで何が起こっているかについて言及していたに違いありません。2つの開開オペコードが存在します:unboxunbox.any

MSDNによると、次の点についてunbox.anyです。

値型のボックス化された形式に適用されると、unbox.any命令は、(タイプOの)obj内に含まれる値を抽出するため、unboxの後にldobjが続くのと同等です。

に関してunbox

[...]オブジェクトから値型をコピーするためにunboxは必要ありません。通常、ボックス化されたオブジェクト内にすでに存在する値型のアドレスを計算するだけです。

それで、著者は彼が何について話しているかを知っていました。

この小さな事実unboxにより、ILを直接操作するときに、特定の気の利いた最適化を行うことができます。たとえば、ref intを受け入れる関数に渡す必要のあるボックス化されたintがある場合は、unboxオペコードを発行するだけで、intへの参照がスタック内で関数の操作に使用できるようになります。この場合、関数はボクシングオブジェクトの実際の内容を変更します。これは、C#レベルではまったく不可能です。これにより、一時ローカル変数にスペースを割り当て、そこでintのボックスを解除し、関数へのintへの参照を渡してから、新しいボックスオブジェクトを作成してintを再ボックス化し、古いボックスを破棄する必要がなくなります。

もちろん、C#レベルで作業している場合、そのような最適化を行うことはできません。したがって、通常発生するのは、コンパイラーによって生成されたコードが、さらに使用する前に、ボックス化されたオブジェクトから変数をコピーすることです。それの。

于 2011-05-31T04:16:07.797 に答える
1

ボクシングは、値型インスタンスを参照型インスタンス(objectまたはインターフェイスのいずれか)にキャストする行為であり、参照型はヒープに割り当てられます。

「C#4.0 in a Nutshell」によると、「... unboxingは、オブジェクトのコンテンツを値型インスタンスにコピーして戻します」。これは、スタック上にあることを意味します。

あなたが参照する記事の中で、著者は次のように述べています。

public static void Main() {

   Int32 v = 5;    // Create an unboxed value type variable
   Object o = v;   // o refers to a boxed version of v
   v = 123;        // Changes the unboxed value to 123

   Console.WriteLine(v + ", " + (Int32) o);    // Displays "123, 5"
}

このコードから、ボクシングの操作がいくつ発生するかを推測できますか?答えが3つであることに驚かれるかもしれません。コードを注意深く分析して、何が起こっているのかを実際に理解しましょう。最初に、Int32のボックス化されていない値型(v)が作成され、5に初期化されます。次に、オブジェクト参照型(o)が作成され、vを指す必要があります。ただし、参照型は常にヒープ内のオブジェクトを指す必要があるため、C#で生成されます。ボックスvへの適切なILコードと、ボックス化されたバージョンのvのアドレスをoに格納します。これで、123がボックス化されなくなり、参照されたデータがボックス化されていない値型vにコピーされます。これは、ボックス化されたバージョンのvには影響しないため、ボックス化されたバージョンは値5を保持します。この例は、oがボックス化されていない方法(oのデータへのポインターを返す)を示していることに注意してください。次に、oのデータは、ボックス化されていない値型vにメモリコピーされます。

于 2010-06-05T01:10:33.237 に答える