メソッド (method)

引数 (arguments)

パラメータ修飾子 (parameter modifier)

メソッドの引数は既定では値渡し (Passing by Value) で、refまたはoutキーワードを指定することで参照渡し (Passing by Reference) となります。

参照型も既定で値渡しですが、それは参照しているアドレスの変更がメソッド外へ影響しないことを意味し、参照型で参照されているデータの変更は呼び出し元へ作用します。

名称が似ていますが、変数の値をコピーして渡す値渡しと、変数の値を直接格納する値型は、異なる概念です。一方で参照を渡す参照渡しと、参照を格納する参照型は近いです。

refとoutの違いは、値の割り当てを強要するタイミングにあります

既定の動作
パラメータ修飾子 引数 (イン) 戻り値 (アウト)
なし (値渡し) コピー  
ref コピー コピー
out   コピー

引数の型による作用の違い

引数が値型で、その値を変更する
public static void Val(int a, ref int b, out int c)
{
    a++;
    b++;
//  c++; // error CS0269: 未割り当ての out パラメーター 'c' が使用されました。

    c = 5;
}

static void Main()
{
    int i1 = 10;
    int i2 = 10;
    int i3 = 10;

    Val(i1, ref i2, out i3);

    Console.Write(i1); // 10 (値型はパラメータ修飾子がないと、呼び出し元へは作用しない)
    Console.Write(i2); // 11
    Console.Write(i3); // 5
}
引数が参照型で、それに格納されているデータを変更する
public class MyClass
{
    public int num;
    public MyClass(int i)
    {
        num = i;
    }
}

public static void Ref1(MyClass a, ref MyClass b, out MyClass c)
{
    a.num++;
    b.num++;
//  c.num++; // error CS0269: 未割り当ての out パラメーター 'c' が使用されました。

    c = new MyClass(5);
}

static void Main()
{
    MyClass c1 = new MyClass(10);
    MyClass c2 = new MyClass(10);
    MyClass c3 = new MyClass(10);

    Ref1(c1, ref c2, out c3);

    Console.Write(c1.num); // 11 (参照型はパラメータ修飾子がなくても、参照しているデータの変更は呼び出し元へ作用する)
    Console.Write(c2.num); // 11
    Console.Write(c3.num); // 5
}
引数が参照型で、そのアドレスを変更する
public static void Ref2(object a, ref object b, out object c)
{
    a = null;
    b = null;
    c = null;
}

static void Main()
{
    object o1 = new object();
    object o2 = new object();
    object o3 = new object();

    Ref2(o1, ref o2, out o3);

    bool r1 = (o1 == null); // false (パラメータ修飾子がないと、参照しているアドレスの変更は呼び出し元へ作用しない)
    bool r2 = (o2 == null); // true
    bool r3 = (o3 == null); // true
}

派生クラスのインスタンスは、参照渡しできない

引数のクラスの派生クラスとなるインスタンスは、refやoutを付加して参照渡しできません。c# - Why doesn't 'ref' and 'out' support polymorphism? - Stack Overflow

class BaseClass {}
class SubClass : BaseClass {}

class Program
{
    static void M1(    BaseClass a) {}
    static void M2(ref BaseClass a) {}
    static void M3(out BaseClass a) {a = null;}

    static void Main(string[] args)
    {
        BaseClass baseClass = new BaseClass();
        M1(baseClass);
        M2(ref baseClass);
        M3(out baseClass);

        SubClass subClass = new SubClass();
        M1(subClass);
        M2(ref subClass); // error CS1503: 引数 1: は 'ref SAMPLE.SubClass' から 'ref SAMPLE.BaseClass' へ変換することはできません。
        M3(out subClass); // error CS1503: 引数 1: は 'out SAMPLE.SubClass' から 'out SAMPLE.BaseClass' へ変換することはできません。
    }
}

可変引数 (variable arguments)

public static string Format (string format, params object[] args);
Format(String, Object[]) - String.Format Method (System) | Microsoft Docs

戻り値 (Return Values)

戻り値の型がvoid以外のとき、returnで値を返せます。また戻り値は引数と異なり、つねに値渡し (変数のコピー) で返されます。

C# 7.0以降、参照戻り値 (ref 戻り値) とすることで参照で返せます。ref 戻り値と ref ローカル変数 (C# ガイド) | Microsoft Docs

オーバーロード (Overloading)

演算子のオーバーロード (Operator overloading)

変換演算子 (Conversion Operator)

クラスや構造体の型変換の方法を定義できます。

implicit 変換演算子

暗黙的な型変換を許容します。それは明示的なキャストは不要であることを示します。

class Foo
{
    private double val;

    public static implicit operator double(Foo foo)
    {
        return foo.val;
    }
}

Foo foo = new Foo();
double num = foo; // 明示的なキャストは不要

次の2つの条件を満たせないならば、明示的なキャストを要求するexplicitを用います。

  • 情報が失われない。たとえば大きい整数型から小さい整数型への変換など
  • 例外を投げない。
explicit 変換演算子

明示的な型変換を強要します。それは明示的なキャストが必要であることを示します。

class Foo
{
    private double val;

    public static explicit operator double(Foo foo)
    {
        return foo.val;
    }
}

Foo foo = new Foo();
double num = (double)foo; // 明示的なキャストが必要

メソッド名の取得

コードから実行位置のメソッド名を取得するには、次のような方法があります。c# - How to get the name of the current method from code - Stack Overflow

string name = System.Reflection.MethodBase.GetCurrentMethod().Name;
public string GetMethodName([System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
{
    return memberName;
}

参考

参考書

MSDN (Microsoft Developer Network) から検索