メソッドの引数 値を渡すか参照を渡すか

基本用法

型と個数を指定

C#言語でメソッドを定義する場合、引数としてどんな型のパラメータを幾つとるのかを決める必要がある


        // 型と個数を指定
        public void Method1(int id,string name)
        {
            // do something
        }

        // 引数をとらない場合は空の括弧
        public void Method2()
        {
            // do something
        }

値渡し

・C#では、メソッドの引数は基本的には値渡しとなる。つまり、呼び元の値のコピーが渡される
・値型の場合は値そのもののコピーが引き渡され、参照型の場合は参照(つまりポインタ)のコピーが渡される
 ・値型の場合は値を書き換えてもメソッドの呼び元のオリジナルの値は書き換わらない
 ・参照型の場合もコピーの参照(ポインタ)を書き換えるのでオリジナルの参照は変わらず値も変化しない

public void Method1(int val)
{
    val = 10;
}
public void Method2(string val)
{
    val = "method";
}
 
public void Tester()
{
    // 値型のサンプル
    int value = 0;
 
    Method1(value);
 
    int assert1 = value; // 値は0
 
    // 参照側のサンプル
    string str = "tester";
 
    Method2(str);
 
    string assert2 = str; // 値は"tester"
}

配列

C#の配列はSystem.Arrayクラスを暗黙に継承した参照型です
値自体、つまりコピーされた参照が変更されても呼び元に影響ないが、
参照が示す先のオブジェクトが変更された場合は、呼び元でも同じアドレスを指しているので、値が変更される
public void Method1(int[] array)
{
    // 配列への参照(ポインタを書き換え)
    array = new int[]{5,6,7};
}
 
public void Method2(int[] array)
{
    // 配列の参照が指すオブジェクト自体を変更
    array[0] = 10;
}
 
public void Tester
{
    int[] array = { 1, 2, 3 };
 
    Method1(array);
 
    int[] assert1 = array; // 中身は同じで 1,2,3
 
    Method2(array);
 
    int[] assert2 = array: // 0番目が書き変って 10,2,3
}

参照渡し

基本は値渡しだが、参照(ポインタ)を渡すことも可能
メソッド定義時にref/outを指定する
呼び元でも明示的にref/outの指定が必要

        public void Method1(ref int val) => val = 10;

        // outパラメタは必ずメソッド内で値を割り当てる必要がある。
        public void Method2(out int val) => val = 10;
 
        public void Tester()
        {
            // ---refのサンプル--------
            // 必ず値を割り当てる必要あり
            int value1 = 0;

            // 呼び元でもref指定
            Method1(ref value1);

            // 値は10
            int assert1 = value1;

            // ---outのサンプル--------

            // 呼び元でもout指定
            // メソッドが抜けた時点で値が割り当てられていることが文法上保証される
            // (メソッド内で変数の定義が可能)
            Method2(out int value2);

            // 値は10
            int assert2 = value2;
        }

可変長

引数の型が同じ場合にparamsという予約語を付与することにより可変長の指定が可能


public void Method(params int[] array)
{
    int sum = array.Sum();
}
 
public void Tester
{
    // 引数は何個でも
    Method();
    Method(1);
    Method(1,2,3);
}

名前付き

複数の引数をとる場合、明示的に名前を付与することにより順番変えられる

public void Method(string path,string file)
{
    var full = Path.Combine(path,file);
}
 
public void Tester
{
    // 引数の順番を指定できる(以下3つは同じ)
    Method("c:\\log","error.log");
    Method(path:"c:\\log",file:"error.log");
    Method(file:"error.log",path:"c:\\log");
}

省略可能

引数にデフォルト値を定義しておくことにより、未指定時にこれを使うように定義することができる
public void Method(string name,string extension=".txt")
{
    var file = name + extension;
}
 
public void Tester
{
    // 明示的指定がない場合はデフォルト値が使用される
    Method("error",".log");
    Method("manual");
}

デリゲート

デリゲート、つまり、処理自体も引数に指定することができる

public void Method(Action action)
{
    Console.WriteLine("Start");
 
    action();
 
    Console.WriteLine("Finish");
}
 
public void Tester
{
    // デリゲートも指定できるので処理自体を渡せる
    Method(()=>Console.WriteLine("DoSomething"));
}

ジェネリック

メソッド定義時にメソッドの型を指定するのだけれど、型を呼び元に指定してもらう方法
メソッドの後ろにwhereで型に条件付与することができる
// ここでは whereによりTがクラス(つまり参照型)であると限定している
public void Method<T>(T val) where T : class
{
    var hoge = val;
}
 
public void Tester
{
    // 参照型(クラスなら何でもOK)
    Method("aaa");
  Method(new Exception());
    //Method(10);  // 値型だとコンパイルエラー
}

拡張メソッド

静的クラスに静的メソッドとして定義する
thisの後に拡張対象のクラスを指定する
public static class Extensions
{
    // 静的メソッドとして定義し引数にthis指定する
    public static void ExtendedMethod(this string s)
    {
        Console.WriteLine("extended :" + s);
    }
}
 
public void Tester
{
   "test".ExtendedMethod();
}