В чем разница между ключевыми словами ref и out? - PullRequest
821 голосов
/ 23 декабря 2008

Я создаю функцию, в которой мне нужно передать объект, чтобы он мог быть изменен функцией. В чем разница между:

public void myFunction(ref MyClass someClass)

и

public void myFunction(out MyClass someClass)

Что мне использовать и почему?

Ответы [ 23 ]

1082 голосов
/ 23 декабря 2008

ref сообщает компилятору, что объект инициализируется перед входом в функцию, а out сообщает компилятору, что объект будет инициализирован внутри функции.

Таким образом, в то время как ref является двухсторонним, out только для внешнего использования.

486 голосов
/ 23 декабря 2008

Модификатор ref означает, что:

  1. Значение уже установлено и
  2. Метод может читать и изменять его.

Модификатор out означает, что:

  1. Значение не установлено и не может быть прочитано методом до тех пор, пока не будет установлено.
  2. Метод должен установить его перед возвратом.
138 голосов
/ 23 марта 2010

Допустим, Дом появляется в кабинете Питера о памятке о докладах TPS.

Если бы Дом был аргументом для ссылки, у него была бы печатная копия памятки.

Если бы Дом был спорным аргументом, он заставил бы Питера напечатать новую копию памятки, которую он мог взять с собой.

51 голосов
/ 23 сентября 2010

Я собираюсь попробовать свои силы в объяснении:

Я думаю, мы понимаем, как работают типы значений, верно? Типы значений (int, long, struct и т. Д.). Когда вы отправляете их в функцию без команды ref, она КОПИРУЕТ данные . Все, что вы делаете с этими данными в функции, влияет только на копию, а не на оригинал. Команда ref отправляет ФАКТИЧЕСКИЕ данные, и любые изменения будут влиять на данные вне функции.

Хорошо, запутанная часть, типы ссылок:

Позволяет создать тип ссылки:

List<string> someobject = new List<string>()

Когда вы обновляетесь Someobject , создаются две части:

  1. Блок памяти, в котором хранятся данные для someobject .
  2. Ссылка (указатель) на этот блок данных.

Теперь, когда вы отправляете someobject в метод без ссылки на него, КОПИРУЕТ указатель reference , НЕ данные. Итак, теперь у вас есть это:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

Две ссылки, указывающие на один и тот же объект. Если вы измените свойство в someobject , используя reference2, это повлияет на те же данные, на которые указывает reference1.

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

Если вы обнулите эталон2 или укажете на новые данные, это не повлияет на эталон1 или на эталон данных1.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

Что происходит, когда вы отправляете someobject по ссылке на метод? фактическая ссылка на someobject отправляется методу. Теперь у вас есть только одна ссылка на данные:

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

Но что это значит? Он действует так же, как отправка объекта не по ссылке, за исключением двух основных вещей:

1) Когда вы обнуляете ссылку внутри метода, она обнуляет ссылку за пределами метода.

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2) Теперь вы можете указать ссылку на совершенно другое местоположение данных, а ссылка вне функции теперь будет указывать на новое местоположение данных.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true
27 голосов
/ 23 декабря 2008

ссылка в и в .

Вы должны использовать out в предпочтениях везде, где этого достаточно для ваших требований.

16 голосов
/ 01 апреля 2016

из

В C # метод может возвращать только одно значение. Если вы хотите вернуть более одного значения, вы можете использовать ключевое слово out. Модификатор out возвращается как ссылка по ссылке. Самый простой ответ заключается в том, что ключевое слово «out» используется для получения значения из метода.

  1. Вам не нужно инициализировать значение в вызывающей функции.
  2. Вы должны присвоить значение в вызываемой функции, иначе компилятор сообщит об ошибке.

исй:

В C #, когда вы передаете тип значения, такой как int, float, double и т. Д., В качестве аргумента параметра метода, он передается по значению. Следовательно, если вы измените значение параметра, оно не повлияет на аргумент в вызове метода. Но если вы пометите параметр ключевым словом «ref», он будет отражен в фактической переменной.

  1. Вам нужно инициализировать переменную перед вызовом функции.
  2. Не обязательно присваивать какое-либо значение параметру ref в методе. Если вы не меняете значение, зачем его помечать как «ref»?
12 голосов
/ 19 мая 2010

Расширение собаки, пример с кошкой. Второй метод с ref изменяет объект, на который ссылается вызывающая сторона. Отсюда и "кот" !!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }
8 голосов
/ 10 февраля 2014

ref и out ведут себя аналогично, за исключением следующих различий.

  • ref переменная должна быть инициализирована перед использованием. out переменная может использоваться без присваивания
  • out должен рассматриваться как неназначенное значение функцией, которая его использует. Таким образом, мы можем использовать инициализированный параметр out в вызывающем коде, но значение будет потеряно при выполнении функции.
8 голосов
/ 23 декабря 2008

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

Пример:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

Пока вы переходите в класс, вам не нужно использовать ref, если вы хотите изменить объект внутри вашего метода.

7 голосов
/ 26 декабря 2015

Для тех, кто учится на примере (как я), вот что Энтони Колесов говорит .

Я создал несколько минимальных примеров ref, out и других, чтобы проиллюстрировать это. Я не освещаю лучшие практики, просто примеры, чтобы понять различия.

https://gist.github.com/2upmedia/6d98a57b68d849ee7091

...