参照渡しのリスト-この動作を説明する


109

次のプログラムを見てください。

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;
    }
}

myListはを通過したと想定refし、出力は

3
4

リストは確かに「参照渡し」ですが、sort機能のみが有効になります。次のステートメントmyList = myList2;は効果がありません。

したがって、出力は実際には次のとおりです。

10
50
100

この動作を説明していただけますか?実際に参照渡しでmyListはない場合(効果がないように見える)、どのように効果がありますか?myList = myList2myList.Sort()

私はそのステートメントでさえ効果がなく、出力が次のようになると想定していました:

100
50
10

単なる観察結果ですが(ここでは問題が単純化されていることを理解しています)、実際に新しいリストを作成している場合は、ChangeLista List<int>ではなくaを返すのが最善のようvoidです。
Jeff B

回答:


110

リストへの参照を渡しています、リスト変数を参照で渡していないため、変数ChangeListを呼び出すと(つまり、参照-「ポインタ」と考える)コピーされ内部のパラメータChangeList はからは見えませんTestMethod

試してください:

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

次に myRef(で宣言されているようにTestMethodローカル変数への参照を渡します。あなたが内部のパラメータを再割り当てする場合は、今、ChangeListあなたも変数再割り当てされている内部を TestMethod


確かにそれは可能ですが、並べ替えがどのように行われているかを知りたいのです
nmdr

6
@Ngm-を呼び出すChangeList、参照のみがコピーされます-同じオブジェクトです。何らかの方法でオブジェクトを変更すると、そのオブジェクトへの参照を持つすべてのオブジェクトに変更が反映されます。
Marc Gravell

225

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

初期状態

次に、並べ替えが適用されます myList.Sort(); コレクションを並べ替え

最後に、次のことを行った場合:myList' = myList2、参照の1つを失いましたが、オリジナルは失い、コレクションはソートされたままでした。

失われた参照

参照で使用する場合(refmyList'myList同じになります(1つの参照のみ)。

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


20

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

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

  • C#では、オブジェクトを渡すことはなく、参照を値で渡します。

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

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


10

このリンクは、C#での参照渡しを理解するのに役立ちます。基本的に、参照型のオブジェクトが値によってメソッドに渡される場合、そのオブジェクトで使用できるメソッドのみがオブジェクトの内容を変更できます。

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

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

(編集:これは上記にリンクされたドキュメントの更新バージョンです。)


5

C#は、問題のオブジェクトが実行されない限りICloneable(値はListクラスでは実行されない)、値で渡されるときに浅いコピーを実行します。

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

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

詳細については、このMSDNの記事「パラメーターの受け渡し」に関する「参照型パラメーターの受け渡し」セクションをお読みください。

StackOverflowの「C#でジェネリックリストを複製する方法」では、リストの詳細なコピーを作成する方法について説明しています。


3

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

これが私の推論です...リストは参照渡しされていますが、値でポインタ変数を渡すと考えてください。ChangeList(myList)を呼び出すと、ポインタが(Global)myListに渡されます。これは(local)myList変数に格納されます。つまり、(local)myListと(global)myListが同じリストを指していることになります。(local)myListが元の(global)myListを参照しているため、これで機能します=>機能します次に、新しいリストを作成し、その(local)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;
    }
}

2

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

パラメータの受け渡しについて理解するには、ここで最も重要なリファレンスを参照てください。
具体的には、これを見て、コードの動作を理解してください。

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

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


0

参照型のオブジェクトに割り当てられるメモリの2つの部分があります。スタックに1つ、ヒープに1つ。スタック内の部分(別名ポインター)には、実際の値が格納されるヒープ内の部分への参照が含まれています。

refキーワードが使用されない場合、スタック内のパーツのコピーが作成され、メソッドに渡されます-ヒープ内の同じパーツへの参照。したがって、ヒープ部分で何かを変更しても、それらの変更は残ります。コピーされたポインターを変更する場合(ヒープ内の他の場所を参照するように割り当てることにより)、メソッド外の元のポインターには影響しません。

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