Ответ 1
Вы можете изменить то, что foo
указывает на использование y
:
Foo foo = new Foo("1");
void Bar(ref Foo y)
{
y = new Foo("2");
}
Bar(ref foo);
// foo.Name == "2"
Я понимаю, что если я передаю значение типа (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]);
Джон Скит написал отличную статью о передаче параметра в С#. Он четко описывает точное поведение и использование передаваемых параметров по значению, по ссылке (ref
) и по выходу (out
).
Здесь важная цитата из этой страницы по параметрам ref
:
Ссылочные параметры не проходят значения переменных, используемых в вызов функции-члена - они используют сами переменные. Скорее, чем создание нового хранилища для переменная в члене функции декларация, то же место хранения, поэтому значение переменной в члене функции и значение опорного параметра будет всегда быть одинаковым. Необходимы справочные параметры модификатор ref как часть как декларация и вызов - это означает, что всегда ясно, когда вы передавая что-то по ссылке.
Очень хорошо объяснено здесь: http://msdn.microsoft.com/en-us/library/s6938f28.aspx
Резюме статьи:
Переменная ссылочного типа не содержит данные непосредственно; Это содержит ссылку на его данные. Когда вы передаете ссылочный тип параметр по значению, можно изменить данные, на которые указывает ссылка, такая как значение члена класса. Однако вы не может изменить значение самой ссылки; то есть вы не можете используйте ту же ссылку для выделения памяти для нового класса и получите ее сохраняются за пределами блока. Для этого передайте параметр, используя ключевое слово ref или out.
Когда вы передаете ссылочный тип с ключевым словом ref, вы передаете ссылку по ссылке, а метод, который вы вызываете, может назначить новое значение для параметра. Это изменение будет распространяться на область вызова. Без ссылки ref эта ссылка передается по значению, и этого не происходит.
С# также имеет ключевое слово '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();
}
Вы также можете использовать out, если вам не нужна ссылка, переданная в:
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;
}
}
В дополнение к существующим ответам:
Как вы просили разницу в двух методах: при использовании 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
}
Параметр в методе, кажется, всегда передает копию, вопрос - это копия чего. Копия выполняется конструктором копирования для объекта, и поскольку все переменные являются Object в С#, я считаю, что это относится ко всем из них. Переменные (объекты) похожи на людей, живущих по некоторым адресам. Мы либо меняем людей, живущих по этим адресам, либо можем создавать больше ссылок на людей, живущих по этим адресам в телефонной книге (делать мелкие копии). Таким образом, более одного идентификатора могут ссылаться на один и тот же адрес. Типы ссылок требуют больше места, поэтому в отличие от типов значений, которые напрямую связаны стрелкой с их идентификатором в стеке, они имеют значение для другого адреса в куче (большее пространство для проживания). Это пространство нужно взять из кучи.
Тип значения: Indentifier (содержит value = адрес значения стека) ---- > Значение типа значения
Тип ссылки: Идентификатор (содержит value = адрес значения стека) ---- > (содержит value = адрес значения кучи) ---- > Значение кучи (чаще всего содержит адреса для других значений), представьте больше стрелок, придерживающихся в разных направлениях в Array [0], массив [1], массив [2]
Единственный способ изменить значение - следовать стрелкам. Если одна стрелка теряется/изменяется в том смысле, что значение недостижимо.
Ссылочные переменные переносят адрес из одного места в другое, поэтому любое их обновление в любом месте отразится на всех местах, КОГДА-то использует REF. Справочная переменная (405) работает до тех пор, пока не будет выделена новая память для справочной переменной, переданной в методе.
После выделения новой памяти (410) изменение значения этого объекта (408) не будет отражаться повсеместно. За этим реф приходит. Ссылка является ссылкой на справочную информацию, поэтому всякий раз, когда выделяется новая память, она узнает, потому что она указывает на это местоположение, поэтому значение может совместно использоваться everyOne. Вы можете увидеть изображение для большей ясности.