C#初心者のための基礎!値型と参照型の違いと使い方をわかりやすく解説#5

当サイトではアフィリエイト広告を利用しています。

C#初級プログラミング

値型と参照型とは?

C#には値型と参照型というものがあります。この2つの違いを理解していないと、今後いろいろと遭遇する不具合の解決ができない場合があったり、思ったような動作をしないことがあるのでしっかり理解しておきましょう。

値型とは

値型とは、数値等の確保するべきバイト数が明確な型で、型を宣言したときに、特定のバイト数をメモリ上に確保する型の事を言います。代表的なもので言うと、byte,short,int,long,float,double,enum,bool等です。

値型の一覧はマイクロソフトの公式サイトに載っているのでこちらを参照してください。
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/value-types-table

この一覧にあるもの以外は参照型です。構造体が値型であることに注意してください。

値型は、宣言した時点で指定したバイト数が確保されます。Int型であれば、intを宣言した時点で、メモリ上に4バイト確保されます。

参照型

参照型は、型を宣言するだけだと何バイト領域を確保するべきかわからないものが参照型になります。代表的なものでいうと、文字列であるstringとクラスです。これらの型は参照型なので、宣言した段階では、値を保持する場所のアドレスのみが確保されます。

値型と参照型の違い

値型と参照型の決定的な違いは、代入したときに顕著に現れます。

値型の場合はintを宣言して、値を「3」で代入し、その値をさらに別のint変数に代入しても、それぞれのint変数が3を保持するため、その後、一方のint変数の値を「4」に変更したとしても、もう一方のint変数は「3」のままで保たれます。これは値型が、それぞれに4バイトのメモリ領域を確保しているからで、それぞれ別の値として動作します。

int a = 10;
int b = 20;

a = b;
b = 30;

MessageBox.Show(a.ToString());
MessageBox.Show(b.ToString());

「a = b;」の部分でaとbは20になり、「b = 30;」を行うとaは20、bは30となります。値型の場合は、特に違和感のない動きをするため、特に注意することはありません。

一方、参照型はアドレスを保持しているだけなので、イコールで結んで代入すると、双方の同じアドレスが入ってしまうため、その後、片方のクラスの値を変更した場合ももう一方の連動して値が変更されたように見えてしまいます。これは、もともと値は1つしかなく、2つのクラス変数が同じアドレスを参照しているのが原因で、連動しているというわけではなく、同じ値を見ているという現象によるものです。このように、参照型を扱うときは、アドレスを保持しているという事を意識してコーディングしないと、別の場所で、連動して値を変更してしまう可能性があります。

Other classA = new Other();
Other classB = new Other();
classA.Value = 10;
classB.Value = 20;

classA = classB;
classB.Value = 30;

MessageBox.Show(classA.Value.ToString());
MessageBox.Show(classA.Value.ToString());

この場合、classAとclassBはクラスなので参照型の変数です。

最初はclassAとclassBは別のインスタンスなので別のアドレスを参照しています。しかし、「classA = classB;」の行で、別のインスタンスをイコールで結んでしまいました。この状態で、classAとclassBは同じものになり、同じアドレスを参照するようになります。そのため、「classB.Value = 30;」を行うと、classBの値を変更しているだけのつもりが、classAのValueも30に変更されてしまいます。このように、参照型の動作を理解していない場合は、classAとclassBが連動しているように見えてしまいますが、連動しているのではなく、同じ場所を見ているのが原因です。

注意点

次のようにメソッドの引数として、参照型を投げる場合も注意が必要です。引数の参照型を、メソッド内部で値の変更をした場合も、呼び出し側に影響が出ます。

Other classC = new Other();
classC.Value = 40;
OtherMethod(classC);
MessageBox.Show(classC.Value.ToString());

private void OtherMethod(Other other)
{
    other.Value = 55;
}

この場合は最初40だった値が、メソッドを呼びだし、その中で55に変更されているため、MessageBox.Showの段階では55に変更されています。

記述例

namespace S5.値型と参照型
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int a = 10;
            int b = 20;

            MessageBox.Show("a=" + a);
            MessageBox.Show("b=" + b);

            a = b;

            MessageBox.Show("a=" + a);
            MessageBox.Show("b=" + b);

            b = 30;
            MessageBox.Show("a=" + a);
            MessageBox.Show("b=" + b);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Other a = new Other();
            Other b = new Other();

            a.Value = 10;
            b.Value = 20;
            MessageBox.Show("a=" + a.Value);
            MessageBox.Show("b=" + b.Value);

            a = b;

            MessageBox.Show("a=" + a.Value);
            MessageBox.Show("b=" + b.Value);

            b.Value = 30;
            MessageBox.Show("a=" + a.Value); //30
            MessageBox.Show("b=" + b.Value); 
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Other a = new Other();
            a.Value = 10;
            MessageBox.Show("a=" + a.Value);

            OtherMethod(a);

            MessageBox.Show("a=" + a.Value); //55
        }

        private void button4_Click(object sender, EventArgs e)
        {
            int a = 10;
            MessageBox.Show("a=" + a);

            OtherMethod2(a);

            MessageBox.Show("a=" + a); //10
        }

        private void OtherMethod(Other other)
        {
            other.Value = 55;
        }

        private void OtherMethod2(int other)
        {
            other = 55;
        }

        public class Other
        {
            public int Value;
        }
    }
}