Каково использование "ref" для ссылочных типов переменных в С#?

Я понимаю, что если я передаю значение типа (int, struct и т.д.) в качестве параметра (без ключевого слова ref), копия этой переменной передается методу, но если Я использую ключевое слово ref, чтобы передать ссылку на эту переменную, а не новую.

Но с ссылочными типами, такими как классы, даже без ключевого слова ref, эта ссылка передается методу, а не копия. Итак, каково использование ключевого слова ref с ссылочными типами?


Возьмем, например:

var x = new Foo();

В чем разница между следующими?

void Bar(Foo y) {
    y.Name = "2";
}

и

void Bar(ref Foo y) {
    y.Name = "2";
}

Ответы

Ответ 1

Вы можете изменить то, что foo указывает на использование y:

Foo foo = new Foo("1");

void Bar(ref Foo y)
{
    y = new Foo("2");
}

Bar(ref foo);
// foo.Name == "2"

Ответ 2

Есть случаи, когда вы хотите изменить фактическую ссылку, а не объект, на который указывает:

void Swap<T>(ref T x, ref T y) {
    T t = x;
    x = y;
    y = t;
}

var test = new[] { "0", "1" };
Swap(ref test[0], ref test[1]);

Ответ 3

Джон Скит написал отличную статью о передаче параметра в С#. Он четко описывает точное поведение и использование передаваемых параметров по значению, по ссылке (ref) и по выходу (out).

Здесь важная цитата из этой страницы по параметрам ref:

Ссылочные параметры не проходят значения переменных, используемых в вызов функции-члена - они используют сами переменные. Скорее, чем создание нового хранилища для переменная в члене функции декларация, то же место хранения, поэтому значение переменной в члене функции и значение опорного параметра будет всегда быть одинаковым. Необходимы справочные параметры модификатор ref как часть как декларация и вызов - это означает, что всегда ясно, когда вы передавая что-то по ссылке.

Ответ 4

Очень хорошо объяснено здесь: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

Резюме статьи:

Переменная ссылочного типа не содержит данные непосредственно; Это содержит ссылку на его данные. Когда вы передаете ссылочный тип параметр по значению, можно изменить данные, на которые указывает ссылка, такая как значение члена класса. Однако вы не может изменить значение самой ссылки; то есть вы не можете используйте ту же ссылку для выделения памяти для нового класса и получите ее сохраняются за пределами блока. Для этого передайте параметр, используя ключевое слово ref или out.

Ответ 5

Когда вы передаете ссылочный тип с ключевым словом ref, вы передаете ссылку по ссылке, а метод, который вы вызываете, может назначить новое значение для параметра. Это изменение будет распространяться на область вызова. Без ссылки ref эта ссылка передается по значению, и этого не происходит.

С# также имеет ключевое слово 'out', которое много похоже на ref, за исключением того, что с 'ref', аргументы должны быть инициализированы перед вызовом метода, а с 'out' вы должны назначить значение в методе приема.

Ответ 6

Он позволяет вам изменить ссылку, переданную в.

void Bar()
{
    var y = new Foo();
    Baz(ref y);
}

void Baz(ref Foo y)
{
    y.Name = "2";

    // Overwrite the reference
    y = new Foo();
}

Вы также можете использовать out, если вам не нужна ссылка, переданная в:

void Bar()
{
    var y = new Foo();
    Baz(out y);
}

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}

Ответ 7

Еще одна группа кода

class O
{
    public int prop = 0;
}

class Program
{
    static void Main(string[] args)
    {
        O o1 = new O();
        o1.prop = 1;

        O o2 = new O();
        o2.prop = 2;

        o1modifier(o1);
        o2modifier(ref o2);

        Console.WriteLine("1 : " + o1.prop.ToString());
        Console.WriteLine("2 : " + o2.prop.ToString());
        Console.ReadLine();
    }

    static void o1modifier(O o)
    {
        o = new O();
        o.prop = 3;
    }

    static void o2modifier(ref O o)
    {
        o = new O();
        o.prop = 4;
    }
}

Ответ 8

В дополнение к существующим ответам:

Как вы просили разницу в двух методах: при использовании ref или out не существует дисперсии co (ntra):

class Foo { }
class FooBar : Foo { }

static void Bar(Foo foo) { }
static void Bar(ref Foo foo) { foo = new Foo(); }

void Main()
{
    Foo foo = null;
    Bar(foo);           // OK
    Bar(ref foo);       // OK

    FooBar fooBar = null;
    Bar(fooBar);        // OK (covariance)
    Bar(ref fooBar);    // compile time error
}

Ответ 9

Параметр в методе, кажется, всегда передает копию, вопрос - это копия чего. Копия выполняется конструктором копирования для объекта, и поскольку все переменные являются Object в С#, я считаю, что это относится ко всем из них. Переменные (объекты) похожи на людей, живущих по некоторым адресам. Мы либо меняем людей, живущих по этим адресам, либо можем создавать больше ссылок на людей, живущих по этим адресам в телефонной книге (делать мелкие копии). Таким образом, более одного идентификатора могут ссылаться на один и тот же адрес. Типы ссылок требуют больше места, поэтому в отличие от типов значений, которые напрямую связаны стрелкой с их идентификатором в стеке, они имеют значение для другого адреса в куче (большее пространство для проживания). Это пространство нужно взять из кучи.

Тип значения: Indentifier (содержит value = адрес значения стека) ---- > Значение типа значения

Тип ссылки: Идентификатор (содержит value = адрес значения стека) ---- > (содержит value = адрес значения кучи) ---- > Значение кучи (чаще всего содержит адреса для других значений), представьте больше стрелок, придерживающихся в разных направлениях в Array [0], массив [1], массив [2]

Единственный способ изменить значение - следовать стрелкам. Если одна стрелка теряется/изменяется в том смысле, что значение недостижимо.

Ответ 10

Ссылочные переменные переносят адрес из одного места в другое, поэтому любое их обновление в любом месте отразится на всех местах, КОГДА-то использует REF. Справочная переменная (405) работает до тех пор, пока не будет выделена новая память для справочной переменной, переданной в методе.

После выделения новой памяти (410) изменение значения этого объекта (408) не будет отражаться повсеместно. За этим реф приходит. Ссылка является ссылкой на справочную информацию, поэтому всякий раз, когда выделяется новая память, она узнает, потому что она указывает на это местоположение, поэтому значение может совместно использоваться everyOne. Вы можете увидеть изображение для большей ясности.

Ref in Reference Variable