ボクシング:
値型は、マネージド ヒープ内のオブジェクトとして割り当てられず、ガベージ コレクションも行われず、ポインターを介して参照されないという点で、参照型よりも「軽量」です。ただし、多くの場合、値型への参照を取得する必要があります。たとえば、一連のポイント構造を保持する ArrayList オブジェクトを作成するとします。コードは次のとおりです。
public static void Main()
{
ArrayList a = new ArrayList();
Point p; //Point を割り当てます (ヒープに割り当てられていません)
for (int i = 0; i {
p.x = p.y = i ; //値の型のメンバーを初期化します
a.Add(p); //値の型をボックス化して ArrayList に参照を追加します
}
}
各ループ反復 ponit 値型フィールドを初期化し、そのポイントを ArrayList に格納します。しかし、ArrayList には正確に何が格納されているか考えてみてください。それは Point 構造体ですか、Point 構造体のアドレスですか、それともまったく別のものですか?正しい答えを知るには、ArrayList の Add メソッドを調べて、そのパラメーターがどのような型として定義されているかを理解する必要があります。この例の Add メソッドのプロトタイプは次のとおりです。
public virtual int Add (Object value);
Add が Object パラメーターを取得する、つまり、Add がオブジェクトへの参照を取得することがわかります。マネージド ヒープをパラメータとして指定します。ただし、前のコードでは、値型である Point である p を渡しました。コードが正しく動作するには、Point 値型をヒープ上でホストされている実際のオブジェクトに変換し、そのオブジェクトへの参照を取得する必要があります。
ボックス化メカニズムを使用して、値の型を参照型に変換します。値型のインスタンスがボックス化されたときに何が起こるかについて説明します。
1. マネージド ヒープにメモリを割り当てます。割り当てられるメモリ量は、値型の各フィールドに必要なメモリ量に、マネージド ヒープ内のすべてのオブジェクトが持つ 2 つの追加メンバー (型オブジェクト ポインターと同期ブロック インデックス) に必要なメモリ量を加えたものです
2 , 値の型 フィールドは新しく割り当てられたヒープメモリ
3にコピーされ、オブジェクトアドレスが返されます。オブジェクトはオブジェクト参照になり、値の型は参照型になります。C# コンパイラは、上記のコードが参照型を必要とするメソッドに値の型を渡していることを検出し、オブジェクトをボックス化するコードを自動的に生成します。したがって、実行時に、Point 値タイプのインスタンス p に現在存在するフィールドが、新しく割り当てられた Point オブジェクトにコピーされます。ボックス化された Point オブジェクト (現在は参照型) のアドレスが返され、Add メソッドに渡されます。ポイント オブジェクトは、ガベージ コレクションが行われるまでヒープ内に残ります。ポイント値型変数 p は、ArrayList がそれについて何も知らないため、再利用できます。この場合、ボックス型の有効期間はボックス値型の有効期間を超えます。
ボックス化解除:
次のコードを使用して ArrayList の最初の要素を取得するとします:
Point p=(Point)a[0];
ArrayList の要素 0 に含まれる参照を取得します。そしてビューは Point 値タイプのインスタンス p に入ります。これを行うには、ボックス化された Point オブジェクト内のすべてのフィールドを、スレッド スタック上にある値型変数にコピーする必要があります。 CLR は 2 つのステップでコピーを完了します。最初のステップは、ボックス化された Point オブジェクト内の各 Point フィールドのアドレスを取得することです。このプロセスはアンボックス化と呼ばれます。 2 番目の部分は、フィールドに含まれる値をヒープからスタックベースの値型インスタンスにコピーします。アンボックス化はボックス化よりもはるかに低コードです。アンボックス化は本質的に、オブジェクトに含まれるプリミティブ値型へのポインターを取得するプロセスです。実際、ポインタはボックス化されたインスタンスのボックス化されていない部分を指します。したがって、ボクシングとは異なり、Chaxiang ではメモリ内にバイトをコピーする必要がありません。この重要な違いを理解した後、知っておくべきもう 1 つの重要な点は、その後にフィールド コピーが行われることが多いということです。
ボックス化された値型インスタンスがアンボックス化されると、内部で次のことが起こります:
1. 「ボックス化された値型への参照」が null の場合、NullReferenceException がスローされます。 object は必要な値型のボックス化されたインスタンスではなく、InvalidCastException がスローされます
2 番目の項目は、コードが予想とは異なる動作をする可能性があることを意味します:
public static void Main()
{
Int32 x = 5;
Object o = x; //Box x, o はボックス化されたオブジェクトを参照します
Int16 y = (Int16)o; //Throw InvalidCastException
}
上記のように、それは完全に可能ですo によって参照されるボックス化された Int32 を取得し、それを強制的に int16 に変換するには、オブジェクトをボックス化解除するときに、元のボックス化されていない値の型 (この場合は Int32) にのみ変換できます。 . :
public static void Main()
{
Int32 x = 5;
Object o = x; //ボックス x, o はボックス化されたオブジェクトを参照します
Int16 y = (Int16)(Int32) o ; //まず正しい型にアンボックスしてから、
}
次のコードを見てください:
public static void Main()
{
Point p;
p.x = p.y = 1;
Object o = p; //ボックス p, o はボックス化されたインスタンスを参照します
//Point の x フィールドを 2 に変更します
p = (Point)o; //ボックス化されたインスタンスからフィールドをスタック変数にコピーします
p.x = 2; //スタック変数の状態を変更します
o = p;スタック変数 p はボックス化され、o は新しいボックス化されたインスタンスを参照します
}
コードの最後の 3 行の唯一の目的は、Point の x フィールドを 1 から 2 に変更することです。これを行うには、最初にボックス化を解除してから、フィールドのコピーを実行し、(スタック上の) フィールドを変更して、最後にボックス化を実行します (マネージド ヒープ上に新しいボックス化されたインスタンスを作成します)。これは、ボックス化とボックス化解除がアプリケーションのパフォーマンスに与える影響を示しています。
質問:
public static void Main()
{
Int32 v = 5;
Object o = v;
v = 123;
Console.WriteLine(v+","+(Int32)o );
}
上記のコードではボクシングが何回発生しますか?
以上が梱包と開梱のチュートリアルの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。