1. 値渡しパラメータ
値渡しでは、実引数の値が仮引数にコピーされてメソッドに渡されます。システムは以下の処理を実行します:
- スタックに仮引数のメモリ領域を確保
- 実引数の値を仮引参にコピー
class DataObject
{
public int Number = 30;
}
class SampleApp
{
static void ProcessValue(DataObject param1, int param2)
{
param1.Number = param1.Number + 10;
param2 = param2 + 10;
Console.WriteLine($"{param1.Number} {param2}");
}
static void Main()
{
DataObject original = new DataObject();
int value = 15;
ProcessValue(original, value);
Console.WriteLine($"{original.Number} {value}");
}
}
実行結果の分析:
- originalオブジェクトは参照型のため、参照がコピーされるだけ。param1とoriginalは同じヒープオブジェクトを指す
- valueは値型のため、コピーが作成される。メソッド内での変更は元の変数に影響しない
2. 参照渡しパラメータ
参照渡しの特徴:
- スタックに新しいメモリ領域は確保されない
- 仮引数は実引数のエイリアスとなり、同じメモリ位置を指す
使用時の注意点:
- 宣言と呼び出しの両方でref修飾子が必要
- 実引数は変数である必要があり、事前に初期化されている必要がある
static void ProcessReference(ref DataObject param1, ref int param2)
{
param1.Number = param1.Number + 10;
param2 = param2 + 10;
Console.WriteLine($"{param1.Number} {param2}");
}
static void Main()
{
DataObject original = new DataObject();
int value = 15;
ProcessReference(ref original, ref value);
Console.WriteLine($"{original.Number} {value}");
}
参照型の値渡しと参照渡しの比較
値渡しで参照型を渡す場合:
static void ModifyObject(DataObject item)
{
item.Number = 100;
item = new DataObject();
Console.WriteLine(item.Number);
}
static void Main()
{
DataObject mainObj = new DataObject();
ModifyObject(mainObj);
Console.WriteLine(mainObj.Number);
}
参照渡しで参照型を渡す場合:
static void ModifyObjectRef(ref DataObject item)
{
item.Number = 100;
item = new DataObject();
Console.WriteLine(item.Number);
}
static void Main()
{
DataObject mainObj = new DataObject();
ModifyObjectRef(ref mainObj);
Console.WriteLine(mainObj.Number);
}
3. 出力パラメータ
出力パラメータはメソッドから呼び出し元へデータを返すために使用されます。ルール:
- 宣言と呼び出しでout修飾子が必要
- 実引数は変数である必要がある
- メソッド内で必ず値を代入する必要がある
static void CalculateValues(out DataObject result1, out int result2)
{
result1 = new DataObject();
result1.Number = 50;
result2 = 25;
}
static void Main()
{
DataObject outputObj;
int outputValue;
CalculateValues(out outputObj, out outputValue);
}
C# 7.0以降では、事前宣言が不要になりました:
static void Main()
{
CalculateValues(out DataObject outputObj, out int outputValue);
outputValue += 10;
Console.WriteLine(outputValue);
}
4. 可変長引数
paramsキーワードを使用して可変数の引数を受け取ることができます:
- パラメータリストの最後にのみ配置可能
- 1つのメソッドに1つのみ使用可能
static void DisplayNumbers(params int[] numbers)
{
if (numbers?.Length > 0)
{
for (int i = 0; i < numbers.Length; i++)
{
numbers[i] = numbers[i] * 2;
Console.WriteLine(numbers[i]);
}
}
}
static void Main()
{
DisplayNumbers(5, 10, 15);
int[] array = {5, 10, 15};
DisplayNumbers(array);
}
5. パラメータタイプの比較
| パラメータタイプ | 修飾子 | 宣言時 | 呼び出し時 | 動作 |
|---|---|---|---|---|
| 値渡し | なし | - | - | 値のコピー |
| 参照渡し | ref | 必須 | 必須 | エイリアスとして参照 |
| 出力 | out | 必須 | 必須 | 戻り値として使用 |
| 可変長 | params | 必須 | 不要 | 可変数の引数 |
6. refローカル変数とref戻り値
refローカル変数
変数のエイリアスを作成できます。値型でも参照として扱えます:
static void Main()
{
int original = 10;
ref int alias = ref original;
Console.WriteLine($"original: {original}, alias: {alias}");
original = 20;
Console.WriteLine($"original: {original}, alias: {alias}");
alias = 30;
Console.WriteLine($"original: {original}, alias: {alias}");
}
ref戻り値
メソッドが値ではなく参照を返すことができます:
class Container
{
private int storedValue = 100;
public ref int GetReference()
{
return ref storedValue;
}
public void ShowValue()
{
Console.WriteLine(storedValue);
}
}
static void Main()
{
Container box = new Container();
box.ShowValue();
ref int externalRef = ref box.GetReference();
externalRef = 200;
box.ShowValue();
}
refの制約事項
- voidメソッドではref戻り値を使用不可
- ローカル変数へのref戻り値は不可
- refローカル変数は一度だけ初期化可能
- 通常のパラメータとして渡す場合は値渡しとなる
public static ref int FindLarger(ref int first, ref int second)
{
return ref (first > second ? ref first : ref second);
}
static void Main()
{
int numA = 50;
int numB = 75;
ref int larger = ref FindLarger(ref numA, ref numB);
larger++;
Console.WriteLine($"numA: {numA}, numB: {numB}");
}