C#を勉強しなおしてみた(4)
値渡し

2024年10月14日

どもです。

本投稿は、以下の投稿の続きモノです。

今回は、「値渡し」について書いていきます。

1. 「値渡し」とは

C#のメソッドの引数に指定された値は、「値渡し」という方法でメソッドで渡されます。「値渡し」では、呼び出されたメソッドに対して、「引数の値のコピー」が渡されます。これは、引数の型が値型でも参照型でも同じです。

この「値渡し」は、名前が似ていることもあり、「値型」と混同されたり、また混乱を招きがちです。今回は、この「値渡し」、「値型」、「参照型」について、コードを交えながら違いを確認してみます。

1.1. 値型の値渡し

まず「値型」の「値渡し」を確認します。確認は、以下のコードで行います。

namespace ValueAndType_001
{
	internal class Program
	{
		static void Main(string[] args)
		{
			int sampleValue = 20;

			SampleMethod(sampleValue);

			Console.WriteLine($"sampleValue = {sampleValue}");

			return;
		}

		static void SampleMethod(int sampleValue)
		{
			sampleValue = 22;

			Console.WriteLine($"sampleValue = {sampleValue}");
		}
	}
}

このコードをビルド、実行した結果が下図です。



Mainから呼び出されたSampleMethod()内では、引数sampleValueの値を変更が変更されています。これに対して呼び出し元のMainメソッドでは、SampleMethodの呼び出し前後で、引数に渡した値が変化していないことが分かります。

1.2. 参照型の値渡し

次に、「参照型」の「値渡し」を確認してみます。

確認は、以下のコートで実施します。

namespace ValueAndRefType_002
{
	public class SampleClass
	{
		public SampleClass()
		{
			SampleValue = 0;
		}

		public int SampleValue { get; set; }
	}

	internal class Program
	{
		static void Main(string[] args)
		{
			var sampleClass = new SampleClass()
			{
				SampleValue = 20
			};
			Console.WriteLine($"In Main : SampleValue = {sampleClass.SampleValue} ");

			SampleMethod(sampleClass);

			Console.WriteLine($"In Main : SampleValue = {sampleClass.SampleValue} ");
		}

		static void SampleMethod(SampleClass sampleClass)
		{
			sampleClass.SampleValue = 22;

			Console.WriteLine($"In Sub  : SampleValue = {sampleClass.SampleValue} ");
		}
	}
}

このコードをビルド、実行した結果が下図です。



SampleMethodメソッドでは、引数で渡されたクラス/オブジェクトのSampleValueプロパティの値を変更してします。引数で指定された値を変更する、という処理に関して言えば、値型のサンプルと同じです。しかし、渡された値が参照型の場合には、SampleMethod関数実行前後で、SampleClassオブジェクトのSampleValueプリパティの値が変化していることが分かります。

1.3. どういうこと?

先述のように、値型と参照型で違いが発生する理由を簡単に書いてみます。

以前の記事で書いた通り、C#の値型、参照型では代入の処理が異なります。メソッド呼び出しの際への引数も、これと同じことが言えます。そのため、「値型」の変数については、値そのもののコピーが引数として渡されます。メソッドの中で引数の値を変更しても、呼び出し元の変数には変更は反映されないのは、このためです。

簡単ですが、イメージとしては下図になります。



一方で「参照型」の変数については、変数の代入の際にには、参照が指すオブジェクト、要は「参照の実体となる値」ではなく「オブジェクトを指す参照」、即ち「オブジェクトのアドレス」が引数に渡されます。メソッドの引数も、同様のことが言えます。メソッドの引数には、「オブジェクトの実体を指すアドレス」が渡されます。そのため、メソッド内で行った場合、同じオブジェクトを参照している呼び出し元のオブジェクトにも影響するのです。メソッド元(Main関数)において、メソッドの呼び出し前後でプロパティの前後で値が変化するのは、このためです。

引数が「参照型」であった場合についても、簡単なイメージを作成してみました。



2. まとめ

今回は、「引数渡し」について書きました。

引数渡しをまとめると、

  • C#のメソッドへの基本的な引数の渡し方
  • 「値渡し」では、「値のコピー」が引数に渡される。
  • 渡す変数が「値型」でも「参照型」でも、メソッドには「値渡し」となる。
  • 渡す変数が「値型」の場合は、「値そのもの」のコピーが引数に渡される。
    (メソッド内で引数を変更しても、呼び出し元の変数は影響を受けない。)
  • 渡す引数が「参照型」の場合には、「オブジェクトへの参照」(アドレス)が渡される。
    (メソッド内でオブジェクトを渡した場合には、呼び出し元の変数が影響を受ける。)

と、できます。

メソッドに渡す値が「値型」か「参照型」かによって、扱いが異なります。そのため、メソッドの呼び出し側、メソッド側で引数を変更しても問題ないのか注意が必要です。

次回は、「Equals」メソッドについて書く予定です。

ではっ!