Как используется “ref” для переменных ссылочного типа в C #?

StackOverflow https://stackoverflow.com/questions/961717

Вопрос

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

Решение

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

Foo foo = new Foo("1");

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

Bar(ref foo);
// foo.Name == "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]);

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

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

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

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

Выдержка из статьи:

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

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

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

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

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

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

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

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

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

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

Еще одна куча кода

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

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

Как вы просили о разнице между двумя методами:При использовании co (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
}

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

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

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

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

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

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

Ref in Reference Variable

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top