まず次のコードを見てください:
int tempi = 1; object o = tempi; double tempd = (double) o;
コンパイル時には成功しますが、実行時には次のエラーが報告されます:
System.InvalidCastException: 指定された変換は無効です。
これは、オブジェクトをアンボックス化するときに、キャストの結果が元のアンボックス化された型でなければならないためです。これを double 型に変換する前に、int 型に変換する必要があります。正しい形式は次のとおりです。
int tempi = 32; object o = tempi; double tempd = (double)(int) o;
.NET Framework では、ボックス化は通常、次の 3 つの手順で構成されます。
1. 新しく生成された参照型オブジェクトにマネージド ヒープからメモリを割り当てます。割り当てられるメモリ サイズは、ボックス化された値型インスタンス自体のサイズに、メソッド テーブル ポインターと、新しく生成された参照型用に追加された SyncBlockIndex を加えたものです。
2. 値型インスタンスのフィールドを、マネージド ヒープ上に新しく割り当てられたオブジェクトのメモリにコピーします。
3. マネージド ヒープ内に新しく割り当てられたオブジェクトのアドレスを返します。このようにして、値型インスタンスも参照型オブジェクトになります。
ボックス化解除のプロセスは次のとおりです。
1. ボックス化解除するオブジェクトが null の場合、NullReferenceException がスローされます。
2. 参照が指すオブジェクトが期待される値型のボックス化されたオブジェクトではない場合、ボックス化解除は失敗し、InvalidCastException がスローされます (この記事の冒頭と同様)。
3. ボックス化されたオブジェクトに含まれる値型部分へのポインタを返します。このポインタが指す値の型は、参照型オブジェクトが通常持つ追加のメンバ (つまり、メソッド テーブル ポインタと SyncBlockIndex) については何も知りません。実際、ポインターは、既にボックス化されたオブジェクトのボックス化されていない部分を指します (Microsoft.NET Framework プログラミング <改訂>)。
ポイント 3 については、上記の例を使用すると理解しやすくなります。まず、メモリ内で 4 バイトを占める値型変数 tempi を定義します。これはボックス化後、参照オブジェクトとなり、メソッド テーブル ポインターと SyncBlockIndex を追加します。参照型の場合、「参照型」のアドレスを渡すだけで、その値、メソッド テーブル ポインター、および SyncBlockIndex を取得できます。アンボックス化の際に渡されるのは、その「値」(ボックス化されていない部分) のアドレス、つまり 4 バイトの読み取りのみを許可する「int (Int32)」型のアドレス (参照) です。 double 型は 8 バイトであるため、暗黙的な変換ではエラーが報告されます。double 型に変換する前に、int 型に変換する必要があります。
.NET でのボックス化とボックス化解除の実装プロセスに関連するその他の記事については、PHP 中国語 Web サイトに注目してください。