Почему список передается без ссылки на функцию, действующую как переданная с помощью ref?

Если я не ошибаюсь, это поведение странно для меня. Вместо того, чтобы объяснять, я отправлю пример кода ниже и, пожалуйста, скажите мне, почему я получаю выходные данные x, а не y.

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

Ответ 1

Он не действует так, как он прошел по ссылке.

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. Вы можете изменить только "список" , если вы передадите его по ссылке (чтобы он указывал в другом месте). Вы получаете копию ссылки, которая, если она изменена, может наблюдаться только внутри вашего метода.

Ответ 2

Параметры передаются по значению в С#, если они не помечены модификаторами ref или out. Для ссылочных типов это означает, что ссылка передается по значению. Поэтому в Fuss, l ссылается на тот же экземпляр List<int>, что и его вызывающий. Поэтому любые модификации этого экземпляра List<int> будут отображаться вызывающим.

Теперь, если вы помечаете параметр l ref или out, тогда параметр передается по ссылке. Это означает, что в Fuss, l является псевдонимом для места хранения, используемого в качестве параметра для вызова метода. Чтобы быть ясным:

public void Fuss(ref List<int> l)

вызываемый

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

Теперь, в Fuss, l является псевдонимом для list. В частности, если вы назначаете новый экземпляр List<int> на l, вызывающий объект увидит, что новый экземпляр присваивается переменной list. В частности, если вы скажете

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

то вызывающий теперь увидит список с одним элементом. Но если вы скажете

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

и вызов

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

то вызывающий объект все равно будет видеть list как имеющий три элемента.

Ясно?

Ответ 3

Разница между ref и non-ref для ссылочных типов, таких как List, заключается не в том, передаете ли вы ссылку (что всегда бывает), но может ли эта ссылка быть изменена. Попробуйте следующее

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

и вы увидите, что счетчик равен 2, потому что функция не только управляет исходным списком, но и самой ссылкой.

Ответ 4

Списки уже являются ссылочными типами, поэтому, передавая их методу, вы передаете ссылку. Любые вызовы Add будут влиять на список в вызывающем абоненте.

Передача List<T> на 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);
    }
}

Ответ 5

ByRef и ByVal применяются только к типам значений, а не к типам ссылок, которые всегда передаются так, как если бы они были "byref".

Если вам нужно модифицировать список незаметно, используйте функцию ".ToList()", и вы получите клон вашего списка.

Имейте в виду, что если ваш список содержит ссылочные типы, ваш "новый" список содержит указатели на те же объекты, что и в вашем исходном списке.

Ответ 6

Переменная, параметр или поле типа "Список" или любой другой ссылочный тип фактически не содержат список (или объект любого другого класса). Вместо этого он будет содержать что-то вроде "Object ID # 29115" (но не такая фактическая строка, конечно, но комбинация бит, что в основном означает это). В другом месте система будет иметь индексированный набор объектов, называемых кучей; если какая-либо переменная типа List содержит "Object ID # 29115", тогда объект # 29115 в куче будет экземпляром List или некоторого типа, полученного из него.

Если MyFoo - это переменная типа List, выражение типа MyFoo.Add( "George" ) на самом деле не изменит MyFoo; вместо этого это означает "Изучить идентификатор объекта, хранящийся в MyFoo, и вызвать метод" Добавить "объекта, хранящегося в нем. Если MyFoo провел" Object ID # 19533 "до выполнения оператора, он будет продолжать делать это позже, но Object У ID # 19533 будет активирован метод Add (возможно, это изменение объекта). И наоборот, оператор типа MyFoo = MyBar заставит MyFoo сохранить тот же идентификатор объекта, что и MyBar, но на самом деле ничего не сделает для объектов в вопрос. Если MyBar провел" Object ID # 59212 "перед оператором, то после утверждения MyFoo также проведет" ObjectId # 59212". Ничего не произойдет с идентификатором объекта # 19533 или с идентификатором объекта # 59212.

Ответ 7

Только примитивные типы, такие как int, double и т.д., передаются по значению.

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