C#の参照型変数の「ref」の使用は何ですか?


176

値タイプ(intstructなど)をパラメーターとして(refキーワードなしで)渡すと、その変数のコピーがメソッドに渡されますが、refキーワードを使用すると、その変数への参照が渡されることを理解しています。新しいものではありません。

ただし、クラスなどの参照型では、refキーワードがなくても、参照はコピーではなくメソッドに渡されます。それではref、参照タイプでのキーワードの使用は何ですか?


例えば:

var x = new Foo();

次の違いは何ですか?

void Bar(Foo y) {
    y.Name = "2";
}

そして

void Bar(ref Foo y) {
    y.Name = "2";
}

回答:


154

foo使用するポイントを変更できますy

Foo foo = new Foo("1");

void Bar(ref Foo y)
{
    y = new Foo("2");
}

Bar(ref foo);
// foo.Name == "2"

17
基本的に元の参照への参照を取得します
lhahne

2
元の参照の「参照先」を変更できるので、そうです。
user7116 2009年

1
クリス、あなたの説明は素晴らしいです。この概念を理解してくれてありがとう。
Andreas Grech、

4
したがって、オブジェクトで「ref」を使用することは、C ++でダブルポインタを使用することに似ていますか?
トム・ヘーゼル

1
@TomHazel:-ish、C ++で「ダブル」ポインターを使用してポインターが指すものを変更している場合。
user7116 2012

29

指しているオブジェクトではなく、実際の参照を変更したい場合があります。

void Swap<T>(ref T x, ref T y) {
    T t = x;
    x = y;
    y = t;
}

var test = new[] { "0", "1" };
Swap(ref test[0], ref test[1]);

21

Jon Skeetは C#でのパラメーターの受け渡しに関するすばらしい記事を書いいます。値、参照(ref)、および出力(out)によって、パラメーターを渡す正確な動作と使用法を明確に説明します。

refパラメータに関するそのページからの重要な引用は次のとおりです。

参照パラメーターは、関数メンバーの呼び出しで使用される変数の値を渡しません-それらは変数自体を使用します。関数メンバー宣言で変数の新しい格納場所を作成するのではなく、同じ格納場所が使用されるため、関数メンバーの変数の値と参照パラメーターの値は常に同じになります。参照パラメーターは、宣言と呼び出しの両方の一部としてref修飾子を必要とします。つまり、参照で何かを渡すときは常に明確です。


11
値渡しの参照を渡すために犬を友達に引き渡すのと同じように私は好きです...しかし、彼があなたに返す前にあなたの友達があなたのシッズをドーベルマンと交換した場合、おそらくあなたおそらく壊れるでしょうからリーシュ;-)
corlettk

16

ここで非常にうまく説明されています:http : //msdn.microsoft.com/en-us/library/s6938f28.aspx

記事の要約:

参照型の変数には、そのデータが直接含まれていません。データへの参照が含まれています。参照型パラメーターを値で渡すと、クラスメンバーの値など、参照が指すデータを変更できます。ただし、参照自体の値を変更することはできません。つまり、同じ参照を使用して新しいクラスにメモリを割り当て、それをブロックの外部に保持させることはできません。そのためには、refまたはoutキーワードを使用してパラメーターを渡します。


4
説明は確かに非常にいいです。ただし、SOではリンクのみの回答はお勧めしません。読者の便宜のため、記事の要約をここに追加しました。
Marcel

10

refキーワードを使用して参照型を渡すと、参照によって参照が渡され、呼び出すメソッドで新しい値をパラメーターに割り当てることができます。その変更は呼び出しスコープに伝播します。refがない場合、参照は値で渡され、これは起こりません。

C#には、refによく似た「out」キーワードもありますが、「ref」ではメソッドを呼び出す前に引数を初期化する必要があり、「out」では受信メソッドに値を割り当てる必要があります。


5

渡された参照を変更できます。例:

void Bar()
{
    var y = new Foo();
    Baz(ref y);
}

void Baz(ref Foo y)
{
    y.Name = "2";

    // Overwrite the reference
    y = new Foo();
}

渡された参照が気にならない場合は、outを使用することもできます。

void Bar()
{
    var y = new Foo();
    Baz(out y);
}

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}

4

別のコードの束

class O
{
    public int prop = 0;
}

class Program
{
    static void Main(string[] args)
    {
        O o1 = new O();
        o1.prop = 1;

        O o2 = new O();
        o2.prop = 2;

        o1modifier(o1);
        o2modifier(ref o2);

        Console.WriteLine("1 : " + o1.prop.ToString());
        Console.WriteLine("2 : " + o2.prop.ToString());
        Console.ReadLine();
    }

    static void o1modifier(O o)
    {
        o = new O();
        o.prop = 3;
    }

    static void o2modifier(ref O o)
    {
        o = new O();
        o.prop = 4;
    }
}

3

既存の回答に加えて:

あなたが2つの方法の違いを求めたように:refまたはを使用する場合、共(ntra)分散はありませんout

class Foo { }
class FooBar : Foo { }

static void Bar(Foo foo) { }
static void Bar(ref Foo foo) { foo = new Foo(); }

void Main()
{
    Foo foo = null;
    Bar(foo);           // OK
    Bar(ref foo);       // OK

    FooBar fooBar = null;
    Bar(fooBar);        // OK (covariance)
    Bar(ref fooBar);    // compile time error
}

1

メソッドのパラメーターは常にコピーを渡すようですが、問題は何のコピーですか。コピーはオブジェクトのコピーコンストラクターによって行われ、すべての変数はC#のオブジェクトであるため、これはすべての変数に当てはまると思います。変数(オブジェクト)は、ある住所に住んでいる人のようなものです。これらの住所に住んでいる人を変更するか、電話帳にそれらの住所に住んでいる人への参照をさらに作成します(浅いコピーを作成します)。したがって、複数の識別子が同じアドレスを参照できます。参照タイプはより多くのスペースを必要とするため、スタック内の識別子に矢印で直接接続されている値タイプとは異なり、ヒープ内の別のアドレスの値(より大きなスペース)があります。このスペースはヒープから取得する必要があります。

値のタイプ:Indentifier(値を含む=スタック値のアドレス)---->値のタイプの値

参照タイプ:識別子(値を含む=スタック値のアドレス)---->(値を含む=ヒープ値のアドレス)---->ヒープ値(ほとんどの場合、他の値へのアドレスを含む)、異なる矢印がくっついていることを想像してくださいArray [0]、Array [1]、array [2]への道順

値を変更する唯一の方法は、矢印に従うことです。1つの矢印が失われたり変更されたりした場合、値に到達できません。


-1

参照変数は、ある場所から別の場所にアドレスを運ぶため、任意の場所でそれらを更新すると、すべての場所に反映され、次にREFの使用方法が反映されます。参照変数(405)は、メソッドで渡された参照変数に新しいメモリが割り当てられなくなるまで有効です。

新しいメモリが割り当てられると(410)、このオブジェクトの値の変更(408)はどこにも反映されません。この参照のために来ます。Refは参照の参照であるため、新しいメモリを割り当てるたびに、その場所を指しているため、その値を知ることができます。したがって、値をeveryOneで共有できます。より鮮明に画像を見ることができます。

参照変数の参照

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.