値型と参照型 ( 構造体 と クラス )

 

値型

変数には直接値格納

ex )
変数 A の値を変数 B へ代入した時、値がコピーされて渡される。
変数 A が持っている値、
変数 b が持っている値、と実体は 2 つある。

 

参照型

変数には「実体どのにあるのかという情報」が格納
実体は別の場所 ( ヒープ )   に確保。

ex )
変数 A の値を変数 B へ代入した時、変数 B には値のアドレスが渡される。
変数 A が持っているのは、値がいる番地001。
変数 B が持っているのも、値がいる番地001。
実体である値は番地 001 にしかいない。つまり実体は 1 つしかない。
複数の場所 ( 変数 ) から1 つの実体をみている。

 

コードで確認

コード内容は、
a, b, c という 3 つの変数に同じ値を代入。
( 同じ値だが、型は値型参照型 )
a, b, c値を画面に出力。
b だけを変更し、再度値を出力。

 

実行すると、
値型では、変更したb の値だけ変わるが、
参照型では、a, b, c の値が変わる。

 

出力結果

f:id:koshinRan:20170805032117j:plain

 

コード

※ button1 をクリックしたら実行される。
※ \n は出力での改行。

    private void button1_Click(object sender, EventArgs e)
    {
        //構造型呼出し (値型)
        Console.Write("値型の場合\n");
        PointV a = new PointV(12, 5);
        PointV b = a;
        PointV c = a;

        Console.Write("  a:{0}\n  b:{1}\n  c:{2}\n", a, b, c);

        b.x = 0; //変更

        Console.WriteLine("  ---");
        Console.Write("  a:{0}\n  b:{1}\n  c:{2}\n", a, b, c);


        //クラス呼出し (参照型)
        Console.Write("\n参照型の場合\n");
        PointReference aa = new PointReference(12, 5);
        PointReference bb = aa;
        PointReference cc = aa;

        Console.Write("  a:{0}\n  b:{1}\n  c:{2}\n", aa, bb, cc);

        bb.x = 0; //変更

        Console.WriteLine("  ---");
        Console.Write("  a:{0}\n  b:{1}\n  c:{2}\n", aa, bb, cc);
    }


    //構造体 ( 値型 )
    struct PointV
    {
        public int x, y;

        public PointV(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
       
        public override string ToString()
        {
            return "(" + this.x.ToString() + ", " + this.y.ToString() + ")";
        }
    }

    //クラス ( 参照型 )
    class PointReference
    {
        public int x, y;

        public PointReference(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        public override string ToString()
        {
            return "(" + this.x.ToString() + ", " + this.y.ToString() + ")";
        }
    }

 

イメージ

f:id:koshinRan:20170805002229j:plain

a, b, cアドレスを持つ
b.x の変更は、b が持つアドレス 000 の実体が変更される。
故に、
変更された実体のアドレス 000 を持つ a, c も変わったように見える。

 

他サイトの説明だと、

f:id:koshinRan:20170805030759j:plain

それぞれの変数は実体への参照のみを持つ。
b.x の変更は、b が参照している実体の値が変更される。
同じ実体を参照している a, c も変更されたように見える。

 

 値型と参照型の利点欠点

値型

欠点
    代入時・引数として渡す時、複製 (コピー) するので
    サイズが大きいと手間がかかる

利点
    値を直接操作できるので、読み書きは高速

 

参照型

利点
    代入時・引数として渡す時、参照情報( 住所 )を渡すので、
    サイスが大きくても手間がかからない

欠点
    値を操作する場合、参照情報を用いて
    実体のある場所を探してから値を操作するので、
    値の読み書きは値型と比べて低速

 

クラスの継承や仮想メソッドなどの
多態的な振る舞いは参照型にしかできない。

 

 

こちらから
http://ufcpp.net/study/csharp/oo_reference.html

 

以上。

---Memo--
構造体とクラスの使い分けは、
データサイズが小さく、継承の必要がないものは構造体。
それ以外はクラス。