Какая польза от «ref» для переменных ссылочного типа в C #? - PullRequest
161 голосов
/ 07 июня 2009

Я понимаю, что если я передам тип значения (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";
}

Ответы [ 10 ]

145 голосов
/ 07 июня 2009

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

Foo foo = new Foo("1");

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

Bar(ref foo);
// foo.Name == "2"
29 голосов
/ 07 июня 2009

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

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]);
17 голосов
/ 07 июня 2009

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

Вот важная цитата с этой страницы в отношении ref параметров:

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

15 голосов
/ 19 февраля 2014

Очень красиво объяснил здесь: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

Аннотация к статье:

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

9 голосов
/ 07 июня 2009

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

C # также имеет ключевое слово «out», которое очень похоже на ref, за исключением того, что с помощью «ref» аргументы должны быть инициализированы перед вызовом метода, а с помощью «out» вы должны присвоить значение в методе получения. 1003 *

5 голосов
/ 07 июня 2009

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

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();
}
4 голосов
/ 07 июня 2009

Еще одна связка кода

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;
    }
}
3 голосов
/ 17 ноября 2016

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

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

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
}
1 голос
/ 07 марта 2014

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

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

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

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

0 голосов
/ 29 июля 2018

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

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

Ref in Reference Variable

...