C#のメソッド引数:実践ガイド

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}");
    }
}

実行結果の分析:

  1. originalオブジェクトは参照型のため、参照がコピーされるだけ。param1とoriginalは同じヒープオブジェクトを指す
  2. 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}");
}

タグ: C# parameters ref out params

7月13日 20:20 投稿