Зачем использовать ключевое слово ' 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; }
}

вывод "бар", что означает, что объект был передан в качестве ссылки.

11 226

11 ответов:

Pass a 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, но относится к совершенно другому объекту.

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

Edit: как уже упоминали другие люди. Это не очень хорошая идея, чтобы использовать 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;
}

вам нужно различать "передача ссылки по значению"и" передача параметра/аргумента по ссылке".

Я написал достаточно большая статья на эту тему чтобы избежать необходимости писать тщательно каждый раз, когда это приходит на группы новостей :)

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

поскольку TestRef является классом (которые являются ссылочными объектами), вы можете изменить содержимое внутри t, не передавая его как ref. Однако, если вы передадите t как ref, TestRef может изменить то, на что ссылается исходный t. т. е. сделать его указать на другой объект.

С ref вы можете написать:

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

и t будут изменены после завершения метода.

подумайте о переменных (например,foo) ссылочных типов (например,List<T>) как удерживая идентификатор объекта формы "объект #24601". Предположим, что утверждение foo = new List<int> {1,5,7,9}; причины foo удерживать "объект #24601" (список из четырех пунктов). Тогда звоните foo.Length спросит объект #24601 для его длины, и он ответит 4, так что foo.Length будет равно 4.

если foo передается в метод без использования ref, этот метод может внести изменения в объект #24601. Как следствие таких изменений,foo.Length может больше не равняться 4. Сам метод, однако не сможет изменить foo, который будет продолжать удерживать "объект #24601".

передает foo Как ref параметр позволит вызываемому методу вносить изменения не только в объект #24601, но и в foo сам по себе. Метод может создать новый объект #8675309 и сохранить ссылку на него в foo. Если это так, foo больше не будет удерживать "объект #24601", но вместо "Объект #8675309".

на практике переменные ссылочного типа не содержат строк вида "Object #8675309"; они даже не содержат ничего, что может быть осмысленно преобразовано в число. Даже если каждая переменная ссылочного типа будет содержать некоторый битовый шаблон, не существует фиксированной связи между битовыми шаблонами, хранящимися в таких переменных, и объектами, которые они идентифицируют. Невозможно, чтобы код мог извлекать информацию из объекта или ссылки на него, а затем определять, является ли другая ссылка идентифицировала тот же самый объект, если только код не содержал или не знал ссылки, которая идентифицировала исходный объект.

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

Это похоже на передачу указателя на указатель в C. В .NET это позволит вам изменить то, что относится к исходному T,лично хотя я думаю, что если вы делаете это в .NET, у вас, вероятно, есть проблема с дизайном!

ref имитирует (или ведет себя) как глобальная область только для двух областей:

  • Caller
  • Callee.

Если вы передаете значение, однако, все по-другому. Вы можете принудительно передать значение по ссылке. Это позволяет передать целое число в метод, например, и заставить метод изменить целое число от вашего имени.

Ref обозначает, может ли функция получить доступ к самому объекту или только к его значению.

передача по ссылке не привязана к языку; это стратегия привязки параметров рядом с pass-by-value, pass by name, pass by need и т. д...

a sidenote: имя класса TestRef - Это ужасно плохой выбор в этом контексте ;).