Список, пройденный 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;
    }
}

Я предположил, что myList прошел бы через ref, а результат будет

3
4

Список действительно "принят ref", но действует только функция sort. Следующий оператор myList = myList2; не имеет эффекта.

Таким образом, вывод:

10
50
100

Можете ли вы помочь мне объяснить это поведение? Если действительно myList не передается по-ref (как это видно из myList = myList2 не действует), как действует myList.Sort()?

Я предполагал, что это утверждение не вступает в силу, а выход будет:

100
50
10

Ответ 1

Вы передаете ссылку на список, но ваш не передает переменную списка по ссылке - поэтому, когда вы вызываете ChangeList значение переменной (т.е. ссылку - думаете "pointer" ) копируется - и изменения значения параметра внутри ChangeList не видны TestMethod.

попробовать:

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

Затем передается ссылка на локальную переменную myRef (как указано в TestMethod); теперь, если вы переназначить параметр внутри ChangeList, вы также переназначаете переменную внутри TestMethod.

Ответ 2

Первоначально он может быть представлен графически следующим образом:

Init states

Затем применяется сортировка myList.Sort(); Sort collection

Наконец, когда вы сделали: myList' = myList2, вы потеряли одну из ссылок, но не оригинал, и коллекция осталась отсортированной.

Lost reference

Если вы используете по ссылке (ref), то myList' и myList станут одинаковыми (только одна ссылка).

Примечание. Я использую myList' для представления параметра, который вы используете в ChangeList (потому что вы дали то же имя, что и оригинал)

Ответ 3

Вот простой способ понять это.

  • Ваш список - это объект, созданный в куче. Переменная myList является ссылку на этот объект.

  • В С# вы никогда не пропускаете объекты, вы передаете их ссылки по значению.

  • При доступе к объекту списка через переданную ссылку в ChangeList (при сортировке, например) исходный список изменяется.

  • Назначение в методе ChangeList выполняется для значения ссылки, поэтому в исходный список не внесены изменения (все еще в куче, но не указана в переменной метода больше).

Ответ 4

эта ссылка поможет вам понять, как пройти по ссылке в С#. В принципе, когда объект ссылочного типа передается по значению методу, только методы, доступные на этом объекте, могут изменять содержимое объекта.

Например, метод List.sort() изменяет содержимое списка, но если вы назначаете какой-либо другой объект той же переменной, это присвоение является локальным для этого метода. Вот почему myList остается неизменным.

Если мы передадим объект ссылочного типа с помощью ключевого слова ref, мы можем назначить некоторый другой объект той же переменной и который изменит весь объект.

Ответ 5

С# только делает мелкую копию, когда она проходит по значению, если только объект, о котором идет речь, не выполняет ICloneable (который, по-видимому, не имеет класса List).

Это означает, что он копирует сам List, но ссылки на объекты внутри списка остаются неизменными; то есть указатели продолжают ссылаться на те же объекты, что и исходный List.

Если вы измените значения своих новых ссылок List, вы также измените оригинальный List (так как он ссылается на одни и те же объекты). Тем не менее, вы затем изменяете то, что myList ссылается полностью, на новый List, и теперь только исходный List ссылается на эти целые числа.

Прочтите раздел "Параметры ссылочного типа передачи" из эту статью MSDN в разделе "Передача параметров" для получения дополнительной информации.

"Как клонировать общий список в С#" из StackOverflow рассказывает о том, как сделать глубокую копию списка.

Ответ 6

Пока я согласен с тем, что все сказали выше. У меня другой подход к этому коду. В основном вы назначаете новый список локальной переменной myList, а не глобальной. если вы измените подпись ChangeList (List myList) на private void ChangeList(), вы увидите результат 3, 4.

Вот мои рассуждения... Несмотря на то, что список передается по ссылке, считайте его передачей переменной указателя по значению Когда вы вызываете ChangeList (myList), вы передаете указатель на (Global) myList. Теперь это сохраняется в переменной (local) myList. Итак, теперь ваш (локальный) myList и (глобальный) myList указывают на тот же список. Теперь вы выполняете сортировку = > он работает, потому что (локальный) myList ссылается на исходный (глобальный) myList Затем вы создаете новый список и назначаете указатель на свой (локальный) myList. Но как только функция выйдет из (локальной) переменной myList, будет уничтожена. НТН

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

Ответ 7

Используйте ключевое слово ref.

Посмотрите на окончательную ссылку здесь, чтобы понять параметры передачи.
Чтобы быть конкретным, посмотрите this, чтобы понять поведение кода.

EDIT: Sort работает по той же ссылке (которая передается по значению), и, следовательно, значения упорядочены. Однако назначение нового экземпляра для параметра не будет работать, потому что параметр передается по значению, если вы не поместите ref.

Ввод ref позволяет вам изменить указатель на ссылку на новый экземпляр List в вашем случае. Без ref вы можете работать с существующим параметром, но не можете указать на что-то еще.