つばろぐ

主に C#, .NET, Azure の備忘録です。たまに日記。

C#におけるref修飾子とout修飾子の違い

はじめに

私は仕事ではC#を主に使用しています。
その中でメソッドのパラメータに付記する修飾子[ref]と[out]は、何が違うのか検証してみます。

ref修飾子

msdnリファレンスより

ref キーワードをつけると、引数の値ではなく参照が渡されます。 参照渡しにより、メソッドの引数への変更が、呼び出し元のメソッドから引数として渡された元の変数へ反映されるようになります。 参照パラメーターの値は、引数の基になる変数の値と常に同じです。

out修飾子

msdnリファレンスより

out キーワードを使用すると、引数が参照渡しされます。 これは ref キーワードに似ていますが、ref の場合は、変数を初期化してから渡す必要があります。 out パラメーターを使用するには、メソッド定義と呼び出し元のメソッドの両方で out キーワードを明示的に使用する必要があります。

違いは?

ref修飾子は対象の変数が初期化されていないとビルドすることができません。
しかしout修飾子は対象の変数が初期化されていなくても問題ありません。

また参照型の変数の場合、out修飾子ではメソッド内部でその変数に対して割り当てる必要があります。
サンプルコードは以下。

public class Person
{
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var me = new Person() { Name = "tsubaki" };
        Console.WriteLine("My name is {0}", me.Name);

        RenameByRef("wells", ref me);
        Console.WriteLine("My name is {0}", me.Name);

        RenameByOut("dann", out me);
        Console.WriteLine("My name is {0}", me.Name);
    }

    private static void RenameByRef(string newName, ref Person me)
    {
        me.Name = newName;
    }

    private static void RenameByOut(string newName, out Person me)
    {
        // コンパイルエラーになります。以下エラーメッセージ。
        //   out パラメーター 'me' はコントロールが現在のメソッドを抜ける前に割り当てられる必要があります。
        me.Name = newName;
    }
}

じゃあout修飾子はどう使う?

例えばメソッド内にて変数[me]を初期化してみましょう。

(Personクラスの定義は同じです)

class Program
{
    static void Main(string[] args)
    {
        var me = new Person() { Name = "tsubaki" };
        Console.WriteLine("My name is {0}", me.Name); // My name is tsubaki

        RenameByRef("wells", ref me);
        Console.WriteLine("My name is {0}", me.Name); // My name is wells

        RenameByOut("dann", out me);
        Console.WriteLine("My name is {0}", me.Name); // My name is dann
    }

    private static void RenameByRef(string newName, ref Person me)
    {
        me.Name = newName;
    }

    private static void RenameByOut(string newName, out Person me)
    {
        me = new Person();
        me.Name = newName;
    }
}

おわりに

参照型の変数に対して何かしらの変更を加える場面が無いとは言えないので、使うとしたらref修飾子でしょうね。
しかし個人的には、これらの修飾子をなるべく使うことが無いような設計をすべきと思いますね。

余談ですが、はてなブログMarkdown記法モードでC#ソースコードを整形して表示するには、```csで始めれば良いみたいです。

参考サイト

ref (C# リファレンス)
out パラメーター修飾子 (C# リファレンス)

独習C# 第3版

独習C# 第3版