アンボクシングは、ヒープの箱入りオブジェクト内の値へのポインターを返すだけですか?
質問
私 このMSDNマガジンの記事, 、著者は(私の強調)を述べています:
ボクシングは常に新しいオブジェクトを作成し、オブジェクトにボックス化されていない値のビットをコピーすることに注意してください。 一方、アンボクシングは、ボックスされたオブジェクト内のデータへのポインターを単に返すだけです。メモリコピーは発生しません。 ただし、通常、コードは、とにかくボックス化されていない参照によって指摘されたデータを指摘することがあります。
私は大胆になった文とそれに続く文に混乱しています。私が読んだ他のすべてから このMSDNページ, 、私はこれまでにヒープの値へのポインターを返すだけだと聞いたことがありません。私は、あなたが最初に始めたように、ボックス化がスタック上の値のコピーを含む変数を持っていると思われるという印象を受けていました。結局のところ、私の変数に「ヒープの値へのポインター」が含まれている場合、値タイプがありません。ポインターがあります。
誰かがこれが何を意味するのかを説明できますか?著者は亀裂にかかっていましたか? (この記事には、少なくとも1つの明白なエラーがあります)。そして、これが真実である場合、「あなたのコードが、とにかくボックス化されていない参照によって指摘されたデータを指摘するデータを引き起こす」ケースは何ですか?
私はちょうどこの記事が10年近く前のものであることに気づいたので、これは.NETの人生の非常に早い段階で変化したものかもしれません。
解決
記事は正確です。しかし、それは何について語っています 本当 コンパイラーが生成するILがどのように見えるかではなく、続行します。結局のところ、.NETプログラムはILを実行することはなく、JITコンパイラによってILから生成されたマシンコードを実行します。
また、Unbox Opcodeは、実際には、値タイプの値を表すヒープ上のビットへのポインターを生成するコードを生成します。 JITは、「jit_unbox」という名前のCLRの小さなヘルパー関数への呼び出しを生成します。 clr src vm jithelpers.cpp sscli20ソースコードを取得した場合。オブジェクト:: getData()関数はポインターを返します。
そこから、値が最初にCPUレジスタにコピーされます。その場合、それはどこかに保管されるかもしれません。スタックである必要はなく、参照型オブジェクト(GCヒープ)のメンバーである可能性があります。または静的変数(ローダーヒープ)。または、スタックでプッシュすることもできます(メソッド呼び出し)。または、CPUレジスタは、式で値が使用されている場合に使用できます。
デバッグ中に、エディターウィンドウを右クリックして、「分解する」を選択して、マシンコードを表示します。
他のヒント
元の記事の著者は、ILレベルで何が起こっているのかについて言及していたに違いありません。 2つのアンボクシングオペコードが存在します。 unbox
と unbox.any
.
MSDNによると、 それにかんする unbox.any
:
値タイプの箱入り形式に適用される場合、unbox.は、obj(タイプoの)内に含まれる値を抽出するため、ldobjがそれに続くアンボックスに相当します。
...]オブジェクトから値タイプをコピーするためにアンボックスは必要ありません。通常、箱入りオブジェクトの内部に既に存在する値タイプのアドレスを計算するだけです。
それで、著者は彼が何について話しているかを知っていました。
この小さな事実について unbox
ILと直接作業するときに、特定の気の利いた最適化を行うことができます。たとえば、ref intを受け入れる関数に渡す必要がある箱入りのintがある場合、あなたはただ放射することができます unbox
OpCode、およびINTへの参照は、機能が動作するようにスタック内で準備ができています。この場合、関数はボクシングオブジェクトの実際の内容を変更します。これは、C#レベルでは非常に不可能です。一時的なローカル変数のためにスペースを割り当て、そこにintを解除し、refを関数に渡す必要性から救い、その後、新しいボクシングオブジェクトを作成して、古いボックスを破棄します。
もちろん、C#レベルで作業している場合、そのような最適化を行うことはできません。そのため、通常はコンパイラによって生成されたコードが、さらに使用する前にボックス付きオブジェクトから変数をコピーすることです。それの。
ボクシングは、参照タイプのインスタンスにバリュータイプのインスタンスをキャストする行為です(どちらかです object
またはインターフェイス)、および参照タイプはヒープに割り当てられます。
「一言で言えば、c#4.0」によると: "... boxing コピー オブジェクトの内容は、値タイプのインスタンスに戻り、それはスタックに意味します。
参照する記事では、著者は次のように述べています。
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を指したいと考えています。ボックスVへの適切なILコードと、o in oのvの箱入りバージョンのアドレスを保存しました。現在、123はボックス化されていません。参照されたデータは、ボックス化されていない値タイプVにコピーされます。これはvのボックスバージョンに影響を与えないため、ボックス付きバージョンは5の値を保持します。この例は、oのボックス化されていない方法を示していることに注意してください(Oのデータへのポインターを返す)、 そして、oのデータは、メモリがボックス化されていない値型vにコピーされます。