Зачем использовать ключевое слово '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 является ужасно плохим выбором в этом контексте;).