メソッドに渡す引数は値渡し、参照渡しのどちらかになります。
値渡し、参照渡し
サイズの小さな変数なら、どちらで渡してもメモリの消費はあまり気にする必要はないと思います。
大きなデータを渡す場合、値渡しだとメモリの消費も馬鹿になりません。
安全性とコスト(メモリの消費)を考えて、どちらを使うか判断するのが良いかと思います。
値渡し
・呼び出し元の変数をコピーして渡すイメージです。
・変数をコピーして渡すので、その分メモリーを消費します。
・変数のコピーなのでメソッドで変更されても呼び出し元の変数に反映されません。
参照渡し
・呼び出し元の変数で確保した領域の場所(住所)を渡すイメージです。
・変数の場所(住所)を渡すだけなので、メモリーを消費しません。
・変数の場所(住所)を渡すので、その場所の変数を変更すると呼び出し元の変数に反映されます。
参照渡しは、ref、in、outの3つにわかれます。
それを整理したのが下の表です。
No | 修飾子 | 値渡し/ 参照渡し | 渡す前の 引数の初期化 | 引数に対する 変更が呼び出し元に 反映されるか | 用途 |
1 | - | 値渡し | 必要 | 反映されない | ・引数に対する変更を許したくない場合 |
2 | ref | 参照渡し | 必要 | 反映される | ・引数に対する変更を必要があれば許す場合 |
3 | in | 参照渡し | 必要 | 変更できない | ・引数に対する変更を許したくない場合 (No1と用途は似ていますが、こちらは変数の コピーが発生しないので、その分メモリーの 消費を節約できます) |
4 | out | 参照渡し | 不要 | 反映される | ・引数に対する変更を求める場合 |
値型と参照型について
値渡し、参照渡し(ref、in、out)の基本的な考え方は上記の説明で大丈夫だと思います。
但し、ここに引数の型が(値型と参照型)が絡んできます。
intなどの値型は上記の説明通りですが、クラスなどの参照型はちょっと違ってきます。
参照型は扱いが元々参照型なので、修飾子なしでも参照型としてメソッドに渡ります。
なので、メソッド内で値を変更すると呼び出し元の値も変更されます。
※ string型はクラスですが特殊で、値型と同じように扱えます。
値型
int、double、DateTime など
参照型
クラス、配列、リストなど
引数の修飾子に ref をつけた場合
・メソッド内で引数の値は変更可能です。
・引数で渡されたオブジェクトの変更(NEW)が可能です。
引数の修飾子に in をつけた場合
・メソッド内で引数の値は変更可能です。
・引数で渡されたオブジェクトの変更(NEW)は不可です。
引数の修飾子に out をつけた場合
・メソッド内で引数の値は変更可能です。
・引数で渡されたオブジェクトの変更(NEW)は必須です。
このような感じです。
プログラミング
値型の基本的なサンプルを作成しました。
言語:C#
修飾子なし
良く見かける変数の渡し方ですね
private void button1_Click(object sender, EventArgs e)
{
int val = 0;
// 渡す前の引数の初期化は必要
sample(val);
Console.WriteLine($"valの値:{val}");
}
private void sample(int val)
{
val = 99; // 引数に対する変更は呼び出し元に反映されない
}
引数に対する変更は呼び出し元に反映されない
実行結果
コンソールの結果で
引数に対する変更は呼び出し元に反映されないことが確認できました。
ref 修飾子
private void button1_Click(object sender, EventArgs e)
{
int val = 0; // 渡す前の引数の初期化は必要
sample(ref val);
Console.WriteLine($"valの値:{val}");
}
private void sample(ref int val)
{
val = 99; // 引数に対する変更は呼び出し元に反映される
}
実行結果
コンソールの結果で
引数に対する変更は呼び出し元に反映されることが確認できました。
in 修飾
private void button1_Click(object sender, EventArgs e)
{
int val = 0; // 渡す前の引数の初期化は必要
sample(in val);
}
private void sample(in int val)
{
// val = 99; 引数に対する変更はできない
}
実行結果
引数の変更はエラーとなりますので、プログラミングで書けません。
なので実行結果はありません。
out 修飾子
private void button1_Click(object sender, EventArgs e)
{
int val; // 渡す前の引数の初期化は不要
sample(out val);
Console.WriteLine($"valの値:{val}");
}
private void sample(out int val)
{
val = 99; // 引数に対する変更は呼び出し元に反映される
}
実行結果
コンソールの結果で
引数に対する変更は呼び出し元に反映されることが確認できました。