Зачем использовать ключевое слово 'ref' при передаче объекта?
Если я передаю объект методу, почему я должен использовать ключевое слово ref? Разве это не поведение по умолчанию?
Например:
class Program
{
static void Main(string[] args)
{
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(t);
Console.WriteLine(t.Something);
}
static public void DoSomething(TestRef t)
{
t.Something = "Bar";
}
}
public class TestRef
{
public string Something { get; set; }
}
Выводится "Bar", что означает, что объект был передан как ссылка.
Ответы
Ответ 1
Передайте ref
, если вы хотите изменить объект:
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);
void DoSomething(ref TestRef t)
{
t = new TestRef();
t.Something = "Not just a changed t, but a completely different TestRef object";
}
После вызова DoSomething t
не относится к оригиналу new TestRef
, но относится к совершенно другому объекту.
Это может быть полезно также, если вы хотите изменить значение неизменяемого объекта, например. a string
. Вы не можете изменить значение string
после его создания. Но с помощью ref
вы можете создать функцию, которая изменяет строку для другой, которая имеет другое значение.
Изменить: как говорили другие люди. Не рекомендуется использовать ref
, если это не требуется. Использование ref
дает возможность свободе выбора аргумента для чего-то другого, вызывающие функции метода должны быть закодированы, чтобы гарантировать, что они справятся с этой возможностью.
Кроме того, когда тип параметра является объектом, переменные объекта всегда действуют как ссылки на объект. Это означает, что при использовании ключевого слова ref
у вас есть ссылка на ссылку. Это позволяет делать вещи, как описано в приведенном выше примере. Но, когда тип параметра является примитивным значением (например, int
), тогда, если этот параметр назначен внутри метода, значение переданного аргумента будет изменено после возврата метода:
int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10
void Change(ref int x)
{
x = 5;
}
void WillNotChange(int x)
{
x = 10;
}
Ответ 2
Вам нужно различать "передачу ссылки по значению" и "передача параметра/аргумента по ссылке".
Я написал достаточно длинную статью на эту тему, чтобы избежать необходимости писать каждый раз, когда это появляется в новостных группах :)
Ответ 3
В .NET при передаче любого параметра методу создается копия. В типах значений означает, что любая модификация, которую вы делаете для значения, находится в области метода и теряется при выходе из метода.
При передаче ссылочного типа также создается копия, но это копия ссылки, т.е. теперь у вас есть две ссылки в памяти для одного и того же объекта. Итак, если вы используете ссылку для изменения объекта, он изменяется. Но если вы сами измените ссылку - мы должны помнить, что это копия - тогда любые изменения также теряются после выхода из метода.
Как уже говорили люди, назначение является модификацией ссылки, поэтому теряется:
public void Method1(object obj) {
obj = new Object();
}
public void Method2(object obj) {
obj = _privateObject;
}
Приведенные выше методы не изменяют исходный объект.
Небольшая модификация вашего примера
using System;
class Program
{
static void Main(string[] args)
{
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(t);
Console.WriteLine(t.Something);
}
static public void DoSomething(TestRef t)
{
t = new TestRef();
t.Something = "Bar";
}
}
public class TestRef
{
private string s;
public string Something
{
get {return s;}
set { s = value; }
}
}
Ответ 4
Так как TestRef - это класс (который является ссылочным объектом), вы можете изменить содержимое внутри t, не передавая его как ref. Однако, если вы передадите t как ref, TestRef может изменить то, что относится к исходному t. т.е. указывать на другой объект.
Ответ 5
С помощью ref
вы можете написать:
static public void DoSomething(ref TestRef t)
{
t = new TestRef();
}
И t будет изменен после завершения метода.
Ответ 6
Подумайте о переменных (например, foo
) ссылочных типов (например, List<T>
) в качестве идентификаторов объектов вида "Объект № 24601". Предположим, что оператор foo = new List<int> {1,5,7,9};
заставляет foo
удерживать "Object # 24601" (список с четырьмя элементами). Затем вызов foo.Length
будет запрашивать объект # 24601 за его длину, и он ответит 4, поэтому foo.Length
будет равно 4.
Если foo
передается методу без использования ref
, этот метод может вносить изменения в объект # 24601. В результате таких изменений foo.Length
может больше не равняться 4. Сам метод, однако, не сможет изменить foo
, который продолжит удерживать "Объект # 24601".
Передача foo
в качестве параметра ref
позволит вызываемому методу вносить изменения не только в объект # 24601, но также и в foo
. Этот метод может создать новый объект # 8675309 и сохранить ссылку на него в foo
. Если это так, foo
больше не будет содержать "Object # 24601", а вместо этого "Object # 8675309".
На практике переменные ссылочного типа не содержат строк формы "Объект № 8675309"; они даже не держат ничего, что может трансформироваться в число. Несмотря на то, что каждая переменная ссылочного типа будет содержать некоторый битовый шаблон, нет фиксированной связи между битовыми шаблонами, хранящимися в таких переменных, и объектами, которые они идентифицируют. Нет способа, чтобы код мог извлекать информацию из объекта или ссылки на него, а затем определять, указывает ли другая ссылка на один и тот же объект, если только код не удерживал или не знал ссылку, которая идентифицировала исходный объект.
Ответ 7
Используя ключевое слово ref
с ссылочными типами, вы эффективно передаете ссылку на ссылку. Во многом это так же, как с использованием ключевого слова out
, но с незначительной разницей, что нет гарантии, что метод фактически присвоит что-либо параметру ref
'ed.
Ответ 8
Это похоже на перенос указателя на указатель на C. В .NET это позволит вам изменить то, что относится к оригинальному Т, лично, хотя я думаю, что если вы делаете это в .NET, у вас, вероятно, есть проблема с дизайном
Ответ 9
ref
имитирует (или ведет себя) как глобальную область только для двух областей:
- Caller
- вызываемая сторона.
Ответ 10
Однако, если вы передаете значение, все по-другому. Вы можете принудительно передать значение по ссылке. Это позволяет, например, передать целое число методу и изменить метод целого числа от вашего имени.
Ответ 11
Ref обозначает, может ли функция получить доступ к самому объекту или только по его значению.
Передача по ссылке не привязана к языку; это стратегия привязки параметров рядом с передачей по значению, передача по имени, передача по необходимости и т.д.
A sidenote: имя класса TestRef
является ужасно плохим выбором в этом контексте;).