ホームページ > バックエンド開発 > C#.Net チュートリアル > C#基礎知識編 基礎知識(18) 値型のボックス化とアンボックス化(1)

C#基礎知識編 基礎知識(18) 値型のボックス化とアンボックス化(1)

黄舟
リリース: 2017-02-11 13:49:43
オリジナル
1334 人が閲覧しました

ボックス化とボックス化解除について詳しく知ることは、実際には非常に興味深いことです。まず、ボックス化とボックス化解除がなぜ起こるのか見てみましょう。
次のコードを見てください:

    class Program
    {
        static void Main(string[] args)
        {
            ArrayList array = new ArrayList();

            Point p;//分配一个

            for (int i = 0; i < 5; i++)
            {
                p.x = i;//初始化值

                p.y = i;

                array.Add(p);//装箱
            }
        }
    }

    public struct Point
    {
        public Int32 x;

        public Int32 y;
    }
ログイン後にコピー

5 回ループし、毎回 Point 値タイプのフィールドを初期化し、それを ArrayList に入れます。 Structは値型の構造体ですが、ArrayListには何が格納されるのでしょうか? ArrayList の Add メソッドをもう一度見てみましょう。 MSDN で Add メソッドを参照できます:
public virtual int Add(Object value),
Add のパラメータが Object 型であることがわかります。つまり、必要なパラメータはオブジェクトへの参照です。つまり、ここでのパラメータは参照型である必要があります。参照型とは何かについては、詳しく説明する必要はありません。それはヒープ上のオブジェクトへの参照にすぎません。ただし、理解を容易にするために、ヒープとスタックについてもう一度話しましょう。
1. スタック領域 (スタック) - コンパイラーによって自動的に割り当ておよび解放され、関数のパラメーター値、ローカル変数値などが格納されます。
2. ヒープ領域(ヒープ) - プログラマによって割り当てられ、解放されます。プログラマが解放しない場合、プログラムの終了時に OS によって再利用される可能性があります。
例:

    class Program
    {
        static void Main(string[] args)
        {
            Int32 n;//这是值类型,存放在栈中,Int32初始值为0
            
            A a;//此时在栈中开辟了空间

            a = new A();//真正实例化后的一个对象则保存在堆中。
        }
    }

    public class A
    {
        public A() { }
    }
ログイン後にコピー

上記の質問に戻りますが、Add メソッドには参照型のパラメーターが必要です。どうすればよいでしょうか?次に、ボックス化を使用する必要があります。いわゆるボックス化とは、値型を参照型に変換することです。変換プロセスは次のとおりです:
1. マネージド ヒープにメモリを割り当てます。割り当てられるメモリ量は、値型の個々のフィールドで必要なメモリ量に、マネージド ヒープ内のすべてのオブジェクトが持つ 2 つの追加メンバー (型オブジェクト ポインターと同期ブロック インデックス) で必要なメモリ量を加えたものです。
2. 値の型フィールドを新しく割り当てられたメモリにコピーします。
3. オブジェクトのアドレスを返します。この時点で、アドレスはオブジェクトへの参照であり、値の型は参照型に変換されています。
このようにして、Add メソッドで、ボックス化された Point オブジェクトへの参照が保存されます。ボックス化されたオブジェクトは、プログラマが処理するか、システムのガベージ コレクションがそれを処理するまで、ヒープ内に残ります。この時点で、ボックス化された値型の有効期間は、ボックス化されていない値型の有効期間を超えます。
上記のボックス化では、配列の 0 番目の要素を取り出したい場合は、当然ボックス化を解除する必要があります。
Point p = (Point)array[0]; ここで行う必要があるのは、参照を取得することです。 ArrayList の要素 0 にそれを Point 値型 p に入れます。この目標を達成するには、どのように実装すればよいでしょうか。まず、ボックス化された Point オブジェクトの各 Point フィールドのアドレスを取得します。開梱はこれで終わりです。これらのフィールドに含まれる値は、ヒープからスタックベースの値型インスタンスにコピーされます。アンボックス化は本質的に、オブジェクトに含まれるプリミティブ値型への参照を取得するプロセスです。実際、参照はボックス化されたインスタンスのボックス化されていない部分を指します。したがって、ボックス化とは異なり、ボックス化解除ではメモリ内のバイトをコピーする必要がありません。ただし、もう 1 つの点があります。ボックス化解除直後にフィールド コピー操作が発生します。
したがって、ボックス化とボックス化解除はプログラムの速度とメモリ消費量に悪影響を与えるため、プログラムがボックス化/ボックス化解除操作を自動的に実行するタイミングに注意し、コードを記述するときにこのような状況を避けるようにしてください。
ボックス化を解除するときは、次の例外に注意してください。
1. 「ボックス化された値型インスタンスへの参照」を含む変数が null の場合、NullReferenceException がスローされます。
2. 参照が指すオブジェクトが期待される値型のボックス化されたインスタンスではない場合、InvalidCastException がスローされます。
たとえば、次のコード スニペット:

             Int32 x = 5;

            Object o = x;

            Int16 r = (Int16)o;//抛出InvalidCastException异常
ログイン後にコピー

これは、ボックス化を解除するときにのみ元のボックス化されていない値の型に変換できるためです。上記のコードを次のように変更します:

             Int32 x = 5;

            Object o = x;

            //Int16 r = (Int16)o;//抛出InvalidCastException异常

            Int16 r = (Int16)(Int32)o;
ログイン後にコピー

現時点では正しいです。

ボックス化解除後、次のコードに示すように、フィールドのコピーが行われます:

            //会发生字段复制
            Point p1;

            p1.x = 1;

            p1.y = 2;

            Object o = p1;//装箱,发生复制

            p1 = (Point)o;//拆箱,并将字段从已装箱的实例复制到栈中
ログイン後にコピー

次のコードセグメントを見てください:

            //要改变已装箱的值

            Point p2;

            p2.x = 10;

            p2.y = 20;

            Object o = p2;//装箱

            p2 = (Point)o;//拆箱

            p2.x = 40;//改变栈中变量的值

            o = p2;//再一次装箱,o引用新的已装箱实例
ログイン後にコピー

ここでの目的は、ボックス化後の p2 の x 値を 40 に変更することです。まず、ボックス化解除が 1 回実行され、フィールドのコピーがスタックに実行され、フィールドの値がスタック上で変更され、次にボックス化が実行されます。その時点で、新しいボックス化されたインスタンスがヒープ上に作成されます。 。このことから、ボックス化/ボックス化解除およびコピーがプログラムのパフォーマンスに与える影響もわかります。

さらにいくつかのボックス化とボックス化解除のコード スニペットを見てみましょう:

            //装箱拆箱演示
            Int32 v = 5;

            Object o = v;

            v = 123;

            Console.WriteLine(v + "," + (Int32)o);
ログイン後にコピー

ここでは 3 つのボックス化が発生していますが、Console.WriteLine でさらに 1 つのボックス化が発生していることがはっきりとわかります。ここの WriteLine には文字列型パラメータがあり、文字列が参照型であることは誰もが知っているため、ここでは (Int32)o をボックス化する必要があります。ここでも、プログラム内で文字列を接続するために + 記号を使用する問題について説明します。接続中に複数の値の型がある場合、いくつかのボックス化操作が実行されます。

ただし、上記のコードは変更できます:

            Object o = v;

            v = 123;
ログイン後にコピー

この方法では、ボックス化は行われません。
次のコードをもう一度見てください:

            //修改后
            Console.WriteLine(v.ToString() + "," + o);
ログイン後にコピー

ここでは 1 つのボックス化、つまり Object o = v のみが発生します。ただし、Console.WriteLine は int、bool、double などでオーバーロードされているため、ここではボックス化は発生しません。

上記はC#の基礎知識の内容です 基礎知識(18) 値型のボックス化とアンボックス化(1) その他の関連内容については、PHP中国語Webサイト(m.sbmmt.com)に注目してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート