web-dev-qa-db-ja.com

refで渡されたリスト-この動作の説明に役立つ

次のプログラムをご覧ください。

class Test
{
    List<int> myList = new List<int>();

    public void TestMethod()
    {
        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList(myList);

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList(List<int> myList)
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}

myListrefを通過し、出力が

3
4

リストは確かに「refed by ref」ですが、sort関数のみが有効になります。次のステートメントmyList = myList2;は効果がありません。

したがって、出力は実際には次のようになります。

10
50
100

この動作の説明を手伝ってもらえますか?本当にmyListpassed-by-refでない場合(myList = myList2が表示されないように)、myList.Sort()はどのように有効になりますか?

私はその声明でさえ効果がなく、出力は次のようになると仮定していました。

100
50
10
94
nmdr

リストにreferenceを渡しています、しかしare n'tリスト変数を渡していますby reference-したがって、ChangeListを呼び出すと、変数の値(つまり、参照-「ポインタ」と考える)がコピーされ、パラメータの値ChangeListare n'tTestMethodから見える。

試してください:

private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);

次に、これはaをlocal-variablemyRefへの参照に渡します(TestMethodで宣言)。現在、ChangeList内でパラメーターを再割り当てすると、変数insideTestMethodも再割り当てされます。

99
Marc Gravell

最初は、次のようにグラフィカルに表現できます。

Init states

次に、並べ替えが適用されますmyList.Sort(); Sort collection

最後に、myList' = myList2を実行すると、参照の1つは失われますが、元の参照は失われず、コレクションはソートされたままになります。

Lost reference

参照(ref)を使用する場合、myList'myListは同じになります(1つの参照のみ)。

注:myList'を使用して、ChangeListで使用するパラメーターを表します(元の名前と同じ名前を付けたため)

199
Jaider

ここにそれを理解する簡単な方法があります

  • リストはヒープ上に作成されたオブジェクトです。変数myListは、そのオブジェクトへの参照です。

  • C#ではオブジェクトを渡すことはありません。値で参照を渡します。

  • ChangeListで渡された参照を介してリストオブジェクトにアクセスすると(たとえば、並べ替え中)、元のリストが変更されます。

  • ChangeListメソッドへの割り当ては参照の値に対して行われるため、元のリストは変更されません(ヒープ上ではまだメソッド変数で参照されていません)。

18

this リンクは、C#の参照渡しを理解するのに役立ちます。基本的に、参照型のオブジェクトが値によってメソッドに渡されると、そのオブジェクトで使用可能なメソッドのみがオブジェクトのコンテンツを変更できます。

たとえば、List.sort()メソッドはリストの内容を変更しますが、他のオブジェクトを同じ変数に割り当てると、その割り当てはそのメソッドに対してローカルになります。そのため、myListは変更されません。

Refキーワードを使用して参照型のオブジェクトを渡すと、他のオブジェクトを同じ変数に割り当てることができ、オブジェクト全体が変更されます。

9
Shekhar

C#は、問題のオブジェクトがICloneable(明らかにListクラスは実行しない)を実行しない限り、値渡し時に浅いコピーを実行します。

つまり、List自体をコピーしますが、リスト内のオブジェクトへの参照は同じままです。つまり、ポインターは元のListと同じオブジェクトを引き続き参照します。

新しいListが参照するものの値を変更すると、元のListも変更されます(同じオブジェクトを参照しているため)。ただし、myListが完全に参照するものを新しいListに変更すると、元のListのみがこれらの整数を参照します。

詳細については、Passing Reference-Type Parametersセクションの このMSDN記事「Passing Parameters」 を参照してください。

「C#で汎用リストを複製する方法」 StackOverflowのリストの詳細コピーの作成方法について説明しています。

5
Ethel Evans

誰もが上で言ったことに同意しますが。私はこのコードについて異なる見解を持っています。 基本的に、グローバルではなくローカル変数myListに新しいリストを割り当てます。 ChangeList(List myList)の署名をprivate void ChangeList()に変更すると、3の出力が表示されます。 4。

ここに私の理由があります...リストは参照で渡されますが、値でポインタ変数を渡すと考えてください。ChangeList(myList)を呼び出すと、ポインタは(Global)myListに渡されます。現在、これは(local)myList変数に格納されています。そのため、(ローカル)myListと(グローバル)myListは同じリストを指します。 (ローカル)myListは元の(グローバル)myListを参照しているため、ソートを実行します。次に、新しいリストを作成し、その(ローカル)myListにポインターを割り当てます。しかし、関数が終了するとすぐに(ローカル)myList変数は破棄されます。 HTH

class Test
{
    List<int> myList = new List<int>();
    public void TestMethod()
    {

        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList();

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList()
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}
3
sandeep

refキーワードを使用します。

決定的なリファレンス here を見て、パラメーターの受け渡しを理解してください。
具体的には、 this を見て、コードの動作を理解してください。

編集:Sortは同じ参照(値によって渡される)で機能するため、値は順序付けられます。ただし、refを指定しない限り、パラメーターは値で渡されるため、パラメーターに新しいインスタンスを割り当てることはできません。

refを指定すると、Listの新しいインスタンスへの参照へのポインターを変更できます。 refがなければ、既存のパラメーターを操作できますが、他のパラメーターを指すようにすることはできません。

2
shahkalpesh