web-dev-qa-db-ja.com

オブジェクトを渡すときに「ref」キーワードを使用する理由

オブジェクトをメソッドに渡す場合、なぜrefキーワードを使用する必要があるのですか?とにかくこれはデフォルトの動作ではありませんか?

例えば:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

出力は「バー」です。これは、オブジェクトが参照として渡されたことを意味します。

255
Ryan

オブジェクトの内容を変更する場合は、refを渡します。

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef t)
{
  t = new TestRef();
  t.Something = "Not just a changed t, but a completely different TestRef object";
}

DoSomethingを呼び出した後、tは元のnew TestRefを参照せず、完全に異なるオブジェクトを参照します。

これは、不変オブジェクトの値を変更する場合にも役立ちます。 stringstringの値は、一度作成すると変更できません。しかし、refを使用することで、異なる値を持つ別の文字列の文字列を変更する関数を作成できます。

編集:他の人が言及したように。必要でない限り、refを使用することはお勧めできません。 refを使用すると、メソッドに他の何かの引数を変更する自由が与えられます。メソッドの呼び出し元は、この可能性を確実に処理するためにコーディングする必要があります。

また、パラメータタイプがオブジェクトの場合、オブジェクト変数は常にオブジェクトへの参照として機能します。つまり、refキーワードを使用すると、参照への参照が得られます。これにより、上記の例で説明されているように実行できます。ただし、パラメータタイプがプリミティブ値(intなど)の場合、このパラメータがメソッド内で割り当てられている場合、渡された引数の値はメソッドが戻った後に変更されます。

int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}
266
Scott Langham

「値による参照の受け渡し」と「参照によるパラメーター/引数の受け渡し」を区別する必要があります。

私は このテーマに関するかなり長い記事 を書きました。これはニュースグループに載るたびに注意深く書く必要がないようにするためです:)

81
Jon Skeet

.NETでメソッドにパラメーターを渡すと、コピーが作成されます。値の型では、値に加えた変更はメソッドスコープで行われ、メソッドを終了すると失われます。

参照タイプを渡すと、コピーも作成されますが、それは参照のコピーです。つまり、メモリ内に同じオブジェクトへの参照が2つあります。したがって、参照を使用してオブジェクトを変更すると、変更されます。ただし、参照自体を変更する場合(コピーであることを覚えておく必要があります)、メソッドを終了すると変更も失われます。

人々が以前に言ったように、割り当ては参照の修正であり、したがって失われます:

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

上記のメソッドは、元のオブジェクトを変更しません。

あなたの例の小さな変更

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }
43
Ricardo Amores

TestRefはクラス(参照オブジェクト)であるため、refとして渡すことなくt内の内容を変更できます。ただし、tをrefとして渡すと、TestRefは元のtが参照するものを変更できます。つまり、別のオブジェクトを指すようにします。

17
Ferruccio

refを使用すると、次のように記述できます。

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

そして、メソッドが完了した後にtが変更されます。

15
Rinat Abdullin

参照型(List<T>など)の変数(foo)は、「オブジェクト#24601」という形式のオブジェクト識別子を保持していると考えてください。ステートメントfoo = new List<int> {1,5,7,9};により、fooが「オブジェクト#24601」(4つの項目を持つリスト)を保持するとします。次に、foo.Lengthを呼び出すと、オブジェクト#24601の長さが要求され、4が返されるため、foo.Lengthは4になります。

fooを使用せずにrefがメソッドに渡されると、そのメソッドはオブジェクト#24601に変更を加える場合があります。このような変更の結果として、foo.Lengthは4に等しくなくなる可能性があります。ただし、メソッド自体は__object_24601を保持し続けるfooを変更できません。

foorefパラメーターとして渡すと、呼び出されたメソッドはオブジェクト#24601だけでなくfoo自体にも変更を加えることができます。このメソッドは、新しいオブジェクト#8675309を作成し、fooにその参照を格納します。その場合、fooは「Object#24601」ではなく、「Object#8675309」を保持します。

実際には、参照型の変数は「Object#8675309」という形式の文字列を保持しません。意味のある数に変換できるものは何も保持していません。各参照型変数はビットパターンを保持しますが、そのような変数に格納されているビットパターンとそれらが識別するオブジェクトとの間に固定された関係はありません。コードがオブジェクトまたはその参照から情報を抽出し、コードが元のオブジェクトを識別する参照を保持または認識していない限り、別の参照が同じオブジェクトを識別するかどうかを後で判断する方法はありません。

7
supercat

これは、Cのポインターにポインターを渡すようなものです。NETでは、元のTが参照するものを変更できます。個人的に問題!

5

refは、2つのスコープに対してのみグローバルエリアとして模倣(または動作)します。

  • 発信者
  • 呼び出し先。
3
guneysus

参照型でrefキーワードを使用すると、参照への参照を効果的に渡すことができます。多くの点で、outキーワードを使用するのと同じですが、メソッドがref 'edパラメーターに実際に何かを割り当てる保証がないという小さな違いがあります。

3
Isak Savo

ただし、値を渡す場合、状況は異なります。参照によって値を強制的に渡すことができます。これにより、たとえば整数をメソッドに渡し、メソッドに代わって整数を変更させることができます。

1
Andrew

Refは、関数がオブジェクト自体を操作できるか、値のみを操作できるかを示します。

参照渡しは言語に拘束されません。これは、値渡し、名前渡し、必要性渡しなどの横にあるパラメータバインド戦略です。

補足:クラス名TestRefは、このコンテキストではひどく悪い選択です;)。

1
xtofl