0

面白い例がありますが、説明してもらえますか?私が理解しているように、文字列は参照クラスですが、変更することはできません。したがって、文字列を変更しようとすると、フレームワークは新しいヒープアドレスへの新しい参照を作成します。Bu私は、メソッドBearからのこの新しいアドレスが外部のメインコールスタックに渡されない理由を理解していません。

class DoWork
{
    public static void Beer(string s1)  {
        s1 = s1 + "beer";
    }
    public static void Vodka(ref string s2) {
        s2 = s2 + "vodka";
    }
}

string sss = "I Like ";
DoWork.Beer(sss);
DoWork.Vodka(ref sss);

したがって、sssには「ウォッカが好き」という値がありますが、なぜですか?

PSまたはこの方法

DoWork.Vodka(ref sss);
DoWork.Beer(sss);

例からのPPSいくつかのILコード

DoWork.Beer:
IL_0000:  ldarg.0     
IL_0001:  ldstr       "beer"
IL_0006:  call        System.String.Concat
IL_000B:  starg.s     00 
IL_000D:  ret

DoWork.Vodka:
IL_0000:  ldarg.0     
IL_0001:  ldarg.0     
IL_0002:  ldind.ref   
IL_0003:  ldstr       "vodka"
IL_0008:  call        System.String.Concat
IL_000D:  stind.ref   
IL_000E:  ret   
4

3 に答える 3

4

簡単に言えば、refキーワードは、呼び出されたメソッドが呼び出しサイトで参照を操作できるようにします。これが の完全な目的ですref

何が起こるかというと、Vodka返されたときに、新しい文字列インスタンスsssに置き換えられています。元のインスタンスは更新されません。モディファイヤなしでは、これは不可能です。ref

これについて話すときによく混乱するのは、参照型を扱うときにパラメータを値参照で渡すという概念について考えるときです。文字列 (参照型)を値で渡す場合、実際に渡すのは何ですか? 変数の値を渡します。この場合、値は文字列への参照です。したがって、その値を渡すと、呼び出し元のメソッドはその値を使用して文字列にアクセスできます。ただし、パラメーターは値で渡されるため、渡されるのは値そのもの (基本的には参照のコピー) であり、値を格納するメモリー位置へのポインターではありません。これが、呼び出し元のメソッドが呼び出しサイトでインスタンスを置き換えることができない理由です。

パラメータを値ではなく参照で渡す場合、呼び出されたメソッドは値が格納されているメモリ位置にアクセスします (値は文字列への参照であることを覚えておいてください)。これは、呼び出されたメソッドがこの値を更新して、呼び出しサイトの変数に別の文字列への参照を含めることができることを意味します。

更新
あなたのコメントから例を分析してみましょう(名前は無実を保護するために変更されています):

class SomeClass
{
    public int Value { get; set; }
}

class DoWork
{
    public static void DoOne(SomeClass c) { c.Value = c.Value + 1; }
    public static void DoTwo(ref SomeClass c) { c.Value = c.Value + 2; }
}

SomeClass参照型です。これは、そのようなインスタンスを渡す任意のコード (明確にするために、参照型の実際のインスタンスを渡すことはなく、インスタンスへの参照を渡す) は、そのインスタンス内のデータを操作できることを意味します*。これは、メソッドを呼び出したり、プロパティ値を設定したりできることを意味し、これがもたらす状態の変化は呼び出しサイトでも表示されます。refキーワードはそれには影響しません。したがって、サンプルでは、ref​​キーワードに違いはありません。ただし、次の変更を検討してください。

class DoWork
{
    public static void DoOne(SomeClass c) 
    { 
        c = new SomeClass(); 
        c.Value = 1; 
    }
    public static void DoTwo(ref SomeClass c) 
    {
        c = new SomeClass(); 
        c.Value = 2; 
    }
}

では、次のように呼び出します。

var temp = new SomeClass() { Value = 42 };
DoWork.DoOne(temp);
Console.WriteLine(temp.Value); // prints 42

DoWork.DoTwo(ref temp);
Console.WriteLine(temp.Value); // prints 2

ここで、両方のメソッドが操作対象の新しいSomeClassインスタンスを作成します。値によって渡された参照を取得するためDoOne、参照の独自のプライベート コピーを更新して、新しいインスタンスを指すようにします。当然、この変更は呼び出しサイトでは見られません。

一方、DoTwoは参照渡しで参照を取得するため、新しいインスタンスを作成して に割り当てるとc、呼び出しサイトが使用しているのと同じメモリ位置を更新するため、temp内部で作成された新しいインスタンスを参照していますDoTwo

要約すると、参照型の場合、使用する必要があるrefのは、呼び出されたメソッドを有効にして、渡されたインスタンス自体を置き換える場合のみです。そのインスタンスの状態のみを操作したい場合、参照型は常にそれを許可します。

Cody Gray が、Jon Skeet のC#でのパラメータ受け渡しへのリンクを投稿しました。まだ読んでいない場合は、読んでください。これは、これらのことを説明するのに非常に優れています (さらに、値の型も扱います)。

* (文字列のように型が不変でない限り、それはまったく別の話です...)

于 2011-02-01T12:28:54.420 に答える
2

文字列へBeerのポインターは値で渡されるためs1 = s1 + "beer";、ポインターのコピーが変更され、メソッドの外部では表示されません。
ただし、Vodka文字列へのポインターは参照によって渡されるためs1 = s1 + "vodka";、同じポインターが変更され、メソッドの外部で表示されます。

于 2011-02-01T12:29:07.947 に答える
2

不変型を見てください

http://codebetter.com/patricksmacchia/2008/01/13/immutable-types-understand-them-and-use-them

于 2011-02-01T12:30:48.910 に答える