17

私がこれをひどく間違えなかったならば、この振る舞いは私にとって奇妙です。説明するのではなく、以下にサンプルコードを投稿し、yではなくxを出力する理由を教えてください。

    private void button1_Click(object sender, EventArgs e)
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(l);
        MessageBox.Show(l.Count.ToString());
    }

    private void Fuss(List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }

出力は3になると思いますが、出力は5になります。これを行うと、出力が5になる可能性があることを理解しています。

    private void button1_Click(object sender, EventArgs e)
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(ref l);
        MessageBox.Show(l.Count.ToString());
    }

    private void Fuss(ref List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }
4

7 に答える 7

17

refによって渡されたようには機能しません。

void ChangeMe(List<int> list) {
  list = new List<int>();
  list.Add(10);
}
void ChangeMeReally(ref List<int> list) {
  list = new List<int>();
  list.Add(10);
}

それを試してみてください。違いに気づきましたか?

リストの内容(または任意の参照型)を変更できるのは、参照なしで渡す場合のみです(他の人が言っているように、ヒープ上のオブジェクトへの参照を渡すため、同じ「メモリ」を変更するため)。

ただし、「list」は変更できません。「list」は、List型のオブジェクトを指す変数です。「リスト」を変更できるのは、参照によって渡す場合のみです(別の場所を指すようにするため)。参照のコピーを取得します。これを変更すると、メソッド内でのみ確認できます。

于 2011-09-06T14:39:26.940 に答える
7

refまたはout修飾子でマークされていない限り、パラメーターはC#の値で渡されます。参照型の場合、これは参照が値によって渡されることを意味します。したがって、ではFuss、はその呼び出し元lと同じインスタンスを参照しています。List<int>したがって、のこのインスタンスへの変更はList<int>、呼び出し元に表示されます。

lここで、パラメーターをrefまたはでマークするとout、パラメーターは参照によって渡されます。これが意味するのは、でFusslがメソッドを呼び出すためのパラメータとして使用される保存場所のエイリアスであるということです。明確にするために:

public void Fuss(ref List<int> l)

によって呼び出されます

List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);

さて、でFusslはのエイリアスですlist。特に、に新しいインスタンスを割り当てるList<int>l、呼び出し元には、変数に割り当てられた新しいインスタンスも表示さlistれます。特に、あなたが言うなら

public void Fuss(ref List<int> l) {
    l = new List<int> { 1 };
}

次に、呼び出し元に1つの要素を含むリストが表示されます。しかし、あなたが言うなら

public void Fuss(List<int> l) {
    l = new List<int> { 1 };
}

と電話

List<int> list = new List<int> { 1, 2, 3 };
Fuss(list);

その場合、呼び出し元はlist3つの要素を持っていると見なされます。

クリア?

于 2011-09-06T14:36:18.817 に答える
3

Listのような参照型のrefとnon-refの違いは、参照を渡すかどうかではなく(常に発生します)、その参照を変更できるかどうかです。次を試してください

private void Fuss(ref List<int> l)
{
    l = new List<int> { 4, 5 };
}

関数が元のリストだけでなく参照自体も操作したため、カウントは2であることがわかります。

于 2011-09-06T14:37:14.400 に答える
3

タイプ「List」の変数、パラメーター、フィールド、またはその他の参照タイプは、実際にはリスト(またはその他のクラスのオブジェクト)を保持しません。代わりに、「オブジェクトID#29115」のようなものを保持します(もちろん、そのような実際の文字列ではありませんが、本質的にそれを意味するビットの組み合わせです)。他の場所では、システムにはヒープと呼ばれるオブジェクトのインデックス付きコレクションがあります。List型の変数が「ObjectID#29115」を保持している場合、ヒープ内のオブジェクト#29115は、Listのインスタンスまたはそこから派生した型になります。

MyFooがList型の変数である場合、「MyFoo.Add( "George")」のようなステートメントは実際にはMyFooを変更しません。代わりに、「MyFooに格納されているオブジェクトIDを調べて、そこに格納されているオブジェクトの「Add」メソッドを呼び出します。ステートメントが実行される前にMyFooが「ObjectID#19533」を保持していた場合、その後も継続しますが、Object ID#19533ではAddメソッドが呼び出されます(おそらくそのオブジェクトが変更されます)。逆に、「MyFoo = MyBar」のようなステートメントは、MyFooにMyBarと同じオブジェクトIDを保持させますが、実際には質問。MyBarがステートメントの前に「ObjectID#59212」を保持していた場合、ステートメントの後に、MyFooも「ObjectId#59212」を保持します。オブジェクトID#19533にもオブジェクトID#59212にも何も起こりません。

于 2011-11-16T18:20:46.963 に答える
2

リストはすでに参照型であるため、リストをメソッドに渡すと、参照が渡されます。すべてAddの呼び出しは、呼び出し元のリストに影響します。

List<T>byを渡すことrefは、基本的にそのリストにダブルポインタを渡すことと同じように動作します。これがイラストです:

using System;
using System.Collections.Generic;

public class Test
{
    public static void Main()
    {
        List<int> l = new List<int>() { 1, 2, 3 };
        Fuss(l);
        Console.WriteLine(l.Count); // Count will now be 5.

        FussNonRef(l);
        Console.WriteLine(l.Count); // Count will still be 5 because we 
                                    // overwrote the copy of our reference 
                                    // in FussNonRef.

        FussRef(ref l);
        Console.WriteLine(l.Count); // Count will be 2 because we changed
                                    // our reference in FussRef.
    }

    private static void Fuss(List<int> l)
    {
        l.Add(4);
        l.Add(5);
    }

    private static void FussNonRef(List<int> l)
    {
        l = new List<int>();
        l.Add(6);
        l.Add(7);
    }

    private static void FussRef(ref List<int> l)
    {
        l = new List<int>();
        l.Add(8);
        l.Add(9);
    }
}
于 2011-09-06T14:35:35.557 に答える
2

ByRefとByValは値型にのみ適用され、参照型には適用されません。参照型は常に「byref」であるかのように渡されます。

リストを慎重に変更する必要がある場合は、「。ToList()」関数を使用すると、リストのクローンが取得されます。

リストに参照型が含まれている場合、「新しい」リストには、元のリストと同じオブジェクトへのポインターが含まれていることに注意してください。

于 2011-09-06T14:42:30.177 に答える
0

int、doubleなどのプリミティブ型のみが値によって渡されます。

複合型(リストなど)は、参照(正確には、値による受け渡しポインター)を介して渡されます。

于 2011-09-06T14:37:27.000 に答える