В чем разница между ключевыми словами "ref" и "out"?

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

public void myFunction(ref MyClass someClass)

а также

public void myFunction(out MyClass someClass)

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

23.12.2008 09:16:43
Вы: Мне нужно передать объект, чтобы он мог быть изменен. Похоже, MyClassэто будет classтип, то есть ссылочный тип. В этом случае передаваемый объект может быть изменен myFunctionдаже без ключевого слова ref/ out. myFunctionполучит новую ссылку, которая указывает на тот же объект, и он может изменять тот же объект столько, сколько он хочет. refКлючевое слово будет иметь различие в том, что myFunctionполучит одну и ту же ссылку на тот же объект. Это было бы важно, только если myFunctionбы изменить ссылку, чтобы указать на другой объект.
Jeppe Stig Nielsen 8.06.2013 13:51:50
Я озадачен количеством запутанных ответов здесь, когда @ AnthonyKolesov's совершенно совершенен.
o0'. 24.12.2013 13:05:29
Объявление метода out полезно, когда вы хотите, чтобы метод возвращал несколько значений. Один аргумент может быть назначен на ноль. Это позволяет методам возвращать значения по желанию.
Yevgraf Andreyevich Zhivago 19.08.2014 02:50:31
Здесь объяснено с помощью примера более понятным :) dotnet-tricks.com/Tutorial/csharp/…
Prageeth godage 8.03.2016 10:46:22
Технически, комментарий @ JeppeStigNielsen является (единственным) правильным ответом на фактический вопрос ОП. Чтобы передать объект в метод, чтобы метод мог модифицировать объект , просто передайте (ссылку на) объект в метод по значению. Изменение объекта в методе с помощью аргумента объекта изменяет исходный объект , даже если метод содержит свою собственную отдельную переменную (которая ссылается на тот же объект).
David R Tribble 29.06.2018 21:32:22
26 ОТВЕТОВ
РЕШЕНИЕ

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

Так что пока refесть два пути, outэто только для внешнего.

1145
21.12.2012 13:54:43
Еще одна интересная особенность out - это то, что функция должна присваивать параметр out. Не разрешается оставлять это без назначения.
Daniel Earwicker 23.12.2008 11:30:17
'ref' применим только к типу значения? Поскольку ссылочный тип всегда передается по ссылке.
faulty 24.12.2008 09:42:07
Да. Типы значений, включая структуры
Rune Grimstad 31.12.2008 09:54:29
@faulty: Нет, ссылка относится не только к типам значений. ref / out похожи на указатели в C / C ++, они имеют дело с расположением в памяти объекта (косвенно в C #) вместо прямого объекта.
thr 15.03.2010 06:53:00
@faulty: нелогично, ссылочные типы всегда передаются по значению в C #, если вы не используете спецификатор ref. Если вы установите myval = somenewval, эффект будет только в этой области действия функции. Ключевое слово ref позволит вам изменить myval, указав somenewval.
JasonTrue 14.04.2010 23:21:15

Реф входит и выходит .

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

28
13.08.2014 11:50:42
не совсем, как принятый ответ ref, если направленный и бесполезный, игнорируя значения-типы, если не передан обратно.
kenny 27.03.2010 13:17:20
@kenny: Не могли бы вы уточнить, пожалуйста, - например, какие слова вы бы изменили, чтобы сохранить дух ответа, но устранить неточность, которую вы ощущаете? Мой ответ не является сумасшедшим предположением новичка, но спешка (краткость, опечатки) в вашем комментарии, кажется, предполагает, что это так. Цель состоит в том, чтобы предоставить способ думать о разнице с наименьшим количеством слов.
Ruben Bartelink 27.03.2010 13:46:48
(Кстати, я знаком с типами значений, ссылочными типами, передачей по ссылке, передачей по значению, COM и C ++, если вам будет полезно сделать ссылку на эти концепции в вашем разъяснении)
Ruben Bartelink 27.03.2010 14:17:17
Ссылки на объекты передаются по значению (кроме случаев, когда используется ключевое слово «ref» или «out»). Думайте об объектах как об идентификационных номерах. Если переменная класса содержит «Object # 1943» и кто-то передает эту переменную по значению в подпрограмму, эта подпрограмма может вносить изменения в Object # 1943, но не может заставить переменную указывать на что-либо, кроме «Object # 1943». Если переменная была передана по ссылке, подпрограмма могла бы удерживать точку переменной «Object # 5441».
supercat 15.01.2012 18:40:43
@supercat: Мне нравится ваше объяснение ref vs val (и эта последующая анаология). Я думаю, что Кенни на самом деле не нужно ничего из того, что ему объяснили, (относительно) сбивает с толку, как и его комментарии. Хотелось бы, чтобы мы все просто удалили эти проклятые комментарии, хотя они просто смущают всех. Основная причина всей этой чепухи заключается в том, что Кенни неправильно прочитал мой ответ и еще не указал ни одного слова, которое следует добавить / удалить / заменить. Никто из нас троих не узнал ничего из дискуссии, которую мы уже не знали, и у другого ответа есть смехотворное количество голосов.
Ruben Bartelink 15.01.2012 23:20:47

Поскольку вы передаете ссылочный тип (класс), в этом нет необходимости, 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если вы хотите изменить объект внутри вашего метода.

8
23.12.2008 10:57:23
Это работает, только если новый объект не создан и не возвращен. Когда создается новый объект, ссылка на старый объект будет потеряна.
etsuba 23.12.2008 15:41:22
Это неправильно - попробуйте следующее: добавить someObject = nullв Barконец выполнить. Ваш код будет работать нормально, так как только Barссылка на экземпляр была обнулена. Теперь измените Barна Bar(ref MyClass someObject)и выполните снова - вы получите, NullReferenceExceptionпотому что Fooссылка на экземпляр также была обнулена.
Keith 27.09.2010 07:55:20

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

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

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

  1. Значение не установлено и не может быть прочитано методом, пока оно не установлено.
  2. Метод должен установить его перед возвратом.
528
21.02.2015 00:19:40
Этот ответ наиболее четко и кратко объясняет ограничения, которые накладывает компилятор при использовании ключевого слова out, а не ключевого слова ref.
Dr. Wily's Apprentice 2.09.2010 15:52:34
Из MSDN: параметр ref должен быть инициализирован перед использованием, в то время как параметр out не должен явно инициализироваться перед передачей, а любое предыдущее значение игнорируется.
Shiva Kumar 20.08.2013 08:33:56
С out, можно ли вообще прочитать его в методе, до того, как он был установлен этим методом, если он был инициализирован до вызова метода? Я имею в виду, может ли вызываемый метод прочитать, что вызывающий метод передал ему в качестве аргумента?
Panzercrisis 25.03.2016 13:46:10
Panzercrisis, для "out", вызываемый метод может прочитать, если он уже установлен. но он должен установить его снова.
robert jebakumar2 30.04.2017 09:06:23

«Бейкер»

Это потому, что первый изменяет вашу строковую ссылку, чтобы указать на «Бейкер». Изменение ссылки возможно, потому что вы передали ее через ключевое слово ref (=> ссылка на ссылку на строку). Второй вызов получает копию ссылки на строку.

Поначалу строка выглядит какой-то особенной. Но строка это просто ссылочный класс, и если вы определите

string s = "Able";

затем s является ссылкой на строковый класс, который содержит текст «Able»! Еще одно присвоение той же переменной через

s = "Baker";

не изменяет исходную строку, а просто создает новый экземпляр и позволяет указывать на этот экземпляр!

Вы можете попробовать это с помощью следующего небольшого примера кода:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

Что вы ожидаете? То, что вы получите, все еще «Able», потому что вы просто устанавливаете ссылку в s на другой экземпляр, в то время как s2 указывает на исходный экземпляр.

РЕДАКТИРОВАТЬ: строка также является неизменной, что означает, что просто не существует метода или свойства, которое изменяет существующий экземпляр строки (вы можете попытаться найти его в документе, но вы не найдете ничего :-)). Все методы работы со строками возвращают новый экземпляр строки! (Вот почему вы часто получаете лучшую производительность при использовании класса StringBuilder)

6
23.12.2008 13:45:33
Точно. Поэтому не совсем верно говорить «Поскольку вы передаете ссылочный тип (класс), нет необходимости использовать ref».
Paul Mitchell 23.12.2008 13:43:16
Теоретически это правильно сказать, потому что он написал «чтобы его можно было изменить», что невозможно в строках. Но из-за неизменяемых объектов «ref» и «out» очень полезны и для ссылочных типов! (.Net содержит много неизменных классов!)
mmmmmmmm 23.12.2008 13:49:49
Да, ты прав. Я не думал о неизменных объектах, таких как строки, потому что большинство объектов являются изменяемыми.
Albic 23.12.2008 16:55:30
Ну, это загадочный ответ, чтобы увидеть в LQP, чтобы быть уверенным; в этом нет ничего плохого, за исключением того, что он кажется длинным и подробным ответом на другой комментарий (поскольку в первоначальном вопросе ни разу не упоминается Able и Baker), как если бы это был форум. Я предполагаю, что это еще не решено еще когда-то.
Nathan Tuggy 25.05.2015 01:35:14

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

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

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

144
29.06.2015 14:48:13
Реф Дом написал бы отчет карандашом, чтобы Питер мог его изменить
Deebster 5.08.2011 09:25:50
@ Дибстер, ты знаешь, эта метафора ничего не сделала для тебя, почему ты так мучаешь это? ;)
Michael Blackburn 8.08.2011 16:09:55
занимательный, но обучающий, stackoverflow нуждается в большем количестве подобных сообщений
Frank Visaggio 26.06.2012 16:54:21
На всякий случай, если кто-то найдет этот ответ только наполовину смешным, пожалуйста, посмотрите фильм «Офисное пространство».
displayName 13.06.2017 19:33:40
и босс Dom и Peters будет стоять здесь за Dom (как аргумент), заставляя обоих работать над его распечатыванием заново, пока Питер не отдаст распечатку Domd
Patrick Artner 21.12.2017 07:05:52

Помните, что над опорным параметром, который передается внутри функции, непосредственно воздействуют.

Например,

    public class MyClass
    {
        public string Name { get; set; }
    }

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

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

Это напишет собака, а не кот. Следовательно, вы должны напрямую работать с некоторым объектом.

-3
26.07.2015 20:30:27
Хотя все здесь в значительной степени верно, на самом деле это не объясняет разницу между значением по ссылке или вне. В лучшем случае он наполовину объясняет разницу между ссылочным и неизменяемым типом.
Conrad Frix 25.12.2012 04:10:46
Если вы хотите, чтобы этот код писал cat, передайте этот объект вместе с ключом 'ref' следующим образом: public static void Bar (ref MyClass someObject), Bar (ref myObject);
Daniel Botero Correa 4.02.2019 06:11:52

Расширяя Собаку, пример Кошки. Второй метод с 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;
    }
13
13.05.2013 16:01:05

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

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

Хорошо, до запутанной части, ссылочные типы:

Давайте создадим ссылочный тип:

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

Когда вы создаете новый объект , создаются две части:

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

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

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

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

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

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

(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 отправляется к методу. Теперь у вас есть только одна ссылка на данные:

(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
56
23.09.2010 17:55:58
Вы имеете в виду, в конце концов (в случае ссылки) есть только одна ссылка на данные, но два псевдонима для них. Правильно?
Sadiq 14.02.2015 05:22:04
Проголосовал за четкое объяснение. Но я думаю , что это не дает ответа на вопрос, как это не объяснить разницу между refи outпараметров.
Joyce Babu 1.09.2016 08:26:08
Удивительно. Можете ли вы объяснить так же, как для outключевого слова?
Asif Mushtaq 27.09.2016 19:06:12

Возможно, я не очень хорош в этом, но, конечно, строки (даже если они технически являются ссылочными типами и живут в куче) передаются по значению, а не по ссылке?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

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

Насколько я знаю, вам нужен только ref для структур / типов значений и самой строки, так как строка является ссылочным типом, который притворяется, что он есть, но не является типом значения.

Я могу быть совершенно не прав, но я новичок.

-4
8.12.2011 18:36:51
Добро пожаловать в Stack Overflow, Эдвин. Насколько я знаю, строки передаются по ссылке, как и любой другой объект. Вы можете быть смущены, потому что строки являются неизменяемыми объектами, поэтому не так очевидно, что они передаются по ссылке. Представьте, что в строке был вызван метод Capitalize(), который изменил бы содержимое строки на заглавные буквы. Если вы затем замените свою строку a = "testing";на a.Capitalize();, то ваш вывод будет "HELLO", а не "Hello". Одним из преимуществ неизменяемых типов является то, что вы можете передавать ссылки и не беспокоиться о том, что другой код изменит значение.
Don Kirkby 8.12.2011 19:42:56
Существует три основных типа семантики, которые может представлять тип: изменяемая ссылочная семантика, изменяемая семантика значений и неизменяемая семантика. Рассмотрим переменные x и y типа T, которые имеют поле или свойство m, и предположим, что x скопирован в y. Если T имеет ссылочную семантику, изменения в xm будут наблюдаться ym. Если T имеет семантику значений, можно изменить xm, не влияя на ym. Если T имеет неизменную семантику, ни xm, ни ym никогда не изменятся. Неизменная семантика может быть смоделирована как объектами ссылки, так и значениями. Строки являются неизменяемыми ссылочными объектами.
supercat 15.01.2012 18:37:46

Они практически одинаковы - единственное отличие состоит в том, что переменная, передаваемая в качестве параметра out, не требует инициализации, а метод, использующий параметр ref, должен установить его в какое-то значение.

int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

Параметры ref предназначены для данных, которые могут быть изменены, а параметры out - для данных, которые являются дополнительным выходом для функции (например, int.TryParse), которая уже использует возвращаемое значение для чего-либо.

1
20.06.2013 16:06:52

Ниже я показал пример использования как Ref, так и out . Теперь вы все будете очищены от реф и вне.

В приведенном ниже примере, когда я комментирую // myRefObj = new myClass {Name = "ref outside named !!"}; line, получит сообщение об ошибке «Использование неназначенной локальной переменной myRefObj» , но в out такой ошибки нет .

Где использовать Ref : когда мы вызываем процедуру с параметром in, и этот же параметр будет использоваться для хранения выходных данных этого процесса.

Где использовать Out: когда мы вызываем процедуру без параметра in и тот же параметр будет использоваться для возврата значения из этого процесса. Также обратите внимание на вывод

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 
1
29.10.2013 15:45:22

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

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

Out: оператор return может использоваться для возврата только одного значения из функции. Однако, используя выходные параметры, вы можете вернуть два значения из функции. Выходные параметры аналогичны ссылочным параметрам, за исключением того, что они передают данные из метода, а не в него.

Следующий пример иллюстрирует это:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

ref: Ссылочный параметр - это ссылка на ячейку памяти переменной. Когда вы передаете параметры по ссылке, в отличие от значений параметров, для этих параметров не создается новое место хранения. Опорные параметры представляют ту же область памяти, что и фактические параметры, которые передаются в метод.

В C # вы объявляете ссылочные параметры, используя ключевое слово ref. Следующий пример демонстрирует это:

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}
5
25.10.2014 16:57:43
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

Вы можете проверить этот код, он опишет вам его полное отличие, когда вы используете «ref», это означает, что вы уже инициализировали эту int / строку

но когда вы используете "out", он работает в обоих условиях, когда вы инициализируете int / string или нет, но вы должны инициализировать int / string в этой функции

1
13.11.2014 11:01:16

ref означает, что значение в параметре ref уже установлено, метод может прочитать и изменить его. Использование ключевого слова ref - это то же самое, что сказать, что вызывающая сторона отвечает за инициализацию значения параметра.


out сообщает компилятору, что за инициализацию объекта отвечает функция, которую функция должна присвоить параметру out. Не разрешается оставлять это без назначения.

6
31.10.2018 09:25:35

С точки зрения метода, который получает параметр, разница между refи в outтом, что C # требует, чтобы методы записывали каждый outпараметр перед возвратом, и не должны ничего делать с таким параметром, кроме передачи его в качестве outпараметра или записи в него до тех пор, пока он не будет передан в качестве outпараметра другому методу или записан напрямую. Обратите внимание, что некоторые другие языки не предъявляют таких требований; виртуальный или интерфейсный метод, который объявлен в C # с outпараметром, может быть переопределен на другом языке, который не накладывает никаких особых ограничений на такие параметры.

С точки зрения вызывающей стороны, C # во многих случаях будет предполагать, что при вызове метода с outпараметром переданная переменная будет записана без предварительного чтения. Это предположение может быть неверным при вызове методов, написанных на других языках. Например:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

Если myDictionaryидентифицирует IDictionary<TKey,TValue>реализацию, написанную на языке, отличном от C #, даже если MyStruct s = new MyStruct(myDictionary);выглядит как присваивание, оно может потенциально остаться sнеизменным.

Обратите внимание, что конструкторы, написанные на VB.NET, в отличие от конструкторов на C #, не предполагают, будут ли вызванные методы изменять какие-либо outпараметры, и безоговорочно очищают все поля. Необычное поведение, упомянутое выше, не произойдет с кодом, написанным полностью на VB или полностью на C #, но может произойти, когда код, написанный на C #, вызывает метод, написанный на VB.NET.

0
11.02.2015 22:51:21

ref и out работают так же, как передача по ссылкам и передача по указателям, как в C ++.

Для ref аргумент должен быть объявлен и инициализирован.

Для, аргумент должен быть объявлен, но может или не может быть инициализирован

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);
4
15.03.2015 10:36:25
Вы можете объявить переменную инлайн: out double Half_nbr.
Sebastian Hofmann 8.02.2018 08:15:02

Ref: ключевое слово ref используется для передачи аргумента в качестве ссылки. Это означает, что когда значение этого параметра изменяется в методе, оно отражается в вызывающем методе. Аргумент, который передается с использованием ключевого слова ref, должен быть инициализирован в вызывающем методе, прежде чем он будет передан вызываемому методу.

Out: ключевое слово out также используется для передачи аргумента, такого как ключевое слово ref, но аргумент может быть передан без присвоения ему значения. Аргумент, который передается с использованием ключевого слова out, должен быть инициализирован в вызываемом методе, прежде чем он вернется к вызывающему методу.

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

Ссылка и выход в методе перегрузки

И ref, и out нельзя использовать одновременно в перегрузке метода. Тем не менее, ref и out обрабатываются по-разному во время выполнения, но они обрабатываются одинаково во время компиляции (CLR не делает различий между ними, пока он создает IL для ref и out).

1
26.07.2015 23:22:38

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

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

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

8
23.05.2017 12:18:28

вне:

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

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

ссылка:

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

  1. Вам нужно инициализировать переменную перед вызовом функции.
  2. Не обязательно присваивать какое-либо значение параметру ref в методе. Если вы не меняете значение, зачем его помечать как «ref»?
18
1.04.2016 18:31:04
«В C # метод может возвращать только одно значение. Если вы хотите вернуть более одного значения, вы можете использовать ключевое слово out». Мы также можем использовать «ref» для возврата значения. Таким образом, мы можем использовать как ref, так и out, если хотим вернуть несколько значений из метода?
Ned 23.05.2018 20:23:46
В C # 7 вы можете вернуть несколько значений с помощью ValueTuples.
Iman Bahrampour 9.12.2018 05:25:53

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

0
5.12.2016 09:57:33

Время создания:

(1) Создаем вызывающий метод Main()

(2) он создает объект List (который является объектом ссылочного типа) и сохраняет его в переменной myList.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

Во время выполнения:

(3) Среда выполнения выделяет память в стеке в # 00, достаточно широкой для хранения адреса (# 00 = myList, поскольку имена переменных на самом деле являются просто псевдонимами для областей памяти)

(4) Runtime создает список объектов в куче в ячейке памяти #FF (все эти адреса, например, sakes)

(5) Среда выполнения затем сохранит начальный адрес #FF объекта в # 00 (или в словах, сохранит ссылку на объект List в указателе myList)

Вернуться к времени разработки:

(6) Затем мы передаем объект List в качестве аргумента myParamListвызываемому методу modifyMyListи назначаем ему новый объект List.

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

Во время выполнения:

(7) Runtime запускает процедуру вызова для вызываемого метода и, как часть его, проверяет тип параметров.

(8) После нахождения ссылочного типа он выделяет память в стеке в # 04 для наложения псевдонима переменной параметра myParamList.

(9) Затем он также сохраняет в нем значение #FF.

(10) Среда выполнения создает объект списка в куче в ячейке памяти # 004 и заменяет #FF в # 04 этим значением (или разыменовывает исходный объект List и указывает на новый объект List в этом методе)

Адрес в # 00 не изменяется и сохраняет ссылку на #FF (или исходный myListуказатель не нарушается).


Ссылок Ключевое слово является директива компилятора , чтобы пропустить генерацию кода во время выполнения для (8) и (9) , что означает , что не будет никакого распределения кучи для параметров метода. Он будет использовать оригинальный указатель # 00 для работы с объектом в #FF. Если исходный указатель не инициализирован, среда выполнения остановится с жалобой на невозможность продолжения, поскольку переменная не инициализирована

Из ключевых слов является директива компилятора , который в значительной степени так же , как исх с небольшим изменением в (9) и (10). Компилятор ожидает, что аргумент не будет инициализирован, и продолжит с (8), (4) и (5), чтобы создать объект в куче и сохранить его начальный адрес в переменной аргумента. Неинициализированная ошибка не будет выдана, а все предыдущие сохраненные ссылки будут потеряны.

4
30.05.2017 10:37:07

Кроме того, что вы можете переназначить чужую переменную другому экземпляру класса, вернуть несколько значений и т. Д., Используя refили outпозволяя кому-либо узнать, что вам нужно от них и что вы намерены делать с предоставленной ими переменной.

  • Вам не нужно ref , или outесли все , что вы собираетесь сделать , это изменить вещи внутри в MyClassслучае , который передается в качестве аргумента someClass.

    • Вызывающий метод увидит изменения типа someClass.Message = "Hello World"того, используете ли вы ref, outили ничего
    • Запись someClass = new MyClass()внутрь myFunction(someClass)заменяет объект, видимый только someClassв области действия myFunctionметода. Вызывающий метод все еще знает об оригинальном MyClassэкземпляре, который он создал и передал вашему методу
  • Вам нужно ref или outесли вы планируете заменить someClassout для нового объекта и хотите, чтобы вызывающий метод увидел ваши изменения

    • Запись someClass = new MyClass()внутри myFunction(out someClass)изменяет объект, видимый методом, который вызвалmyFunction

Другие программисты существуют

И они хотят знать, что вы собираетесь делать со своими данными. Представьте, что вы пишете библиотеку, которая будет использоваться миллионами разработчиков. Вы хотите, чтобы они знали, что вы собираетесь делать со своими переменными, когда они вызывают ваши методы

  • Использование refделает утверждение «Передайте переменную, назначенную некоторому значению, когда вы вызываете мой метод. Имейте в виду, что я могу полностью изменить его на что-то другое в течение моего метода. Не ожидайте, что ваша переменная будет указывать на старый объект». когда я уже закончил"

  • Использование outделает утверждение «Передать переменную-заполнитель моему методу. Неважно, имеет ли оно значение или нет; компилятор заставит меня присвоить ему новое значение. Я абсолютно гарантирую, что объект, на который указывает ваш переменная, прежде чем вы вызвали мой метод, будет разным к тому времени, как я закончу

Кстати, в C # 7.2 тоже есть inмодификатор

И это не позволяет методу поменять местами переданный экземпляр для другого экземпляра. Подумайте об этом, как если бы вы сказали тем миллионам разработчиков: «Передайте мне исходную ссылку на переменную, и я обещаю не менять ваши тщательно созданные данные на что-то другое». inимеет некоторые особенности, и в некоторых случаях, например, когда может потребоваться неявное преобразование, чтобы сделать ваш шорт совместимым с in intкомпилятором, он временно сделает int, расширит ваш шорт, передаст его по ссылке и завершит работу. Это может быть сделано, потому что вы заявили, что не будете с этим связываться.


Microsoft сделала это с помощью .TryParseметодов числовых типов:

int i = 98234957;
bool success = int.TryParse("123", out i);

Отметив этот параметр, который outони здесь активно заявляют, «мы определенно собираемся заменить ваше кропотливое значение 98234957 на что-то другое»

Конечно, они вроде как должны, для таких вещей, как синтаксический анализ типов значений, потому что если бы методу parse не было разрешено поменять тип значения на что-то другое, это не сработало бы очень хорошо ... Но представьте, что в некоторых есть какой-то фиктивный метод библиотека, которую вы создаете:

public void PoorlyNamedMethod(out SomeClass x)

Вы можете видеть, что это out, и, таким образом, вы можете знать, что, если вы часами работаете с цифрами, создавая идеальный SomeClass:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

Ну, это была пустая трата времени, потратить все эти часы, чтобы сделать этот идеальный урок. Это определенно будет отброшено и заменено PoorlyNamedMethod

3
14.08.2019 14:52:33

Чтобы проиллюстрировать множество превосходных объяснений, я разработал следующее консольное приложение:

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
  • AppendWorld: Копия StringListимени LiStriпередана. В начале метода эта копия ссылается на исходный список и, следовательно, может использоваться для изменения этого списка. Позже LiStriссылается на другой List<string>объект внутри метода, который не влияет на исходный список.

  • HalloWelt: LiStriRefэто псевдоним уже инициализированного ListStringRef. Переданный List<string>объект используется для инициализации нового, поэтому refбыл необходим.

  • CiaoMondo: LiStriOutявляется псевдонимом ListStringOutи должен быть инициализирован.

Таким образом, если метод просто изменяет объект, на который ссылается переданная переменная, компилятор не разрешит вам его использовать, outи вам не следует его использовать, refпотому что это может сбить с толку не компилятор, а читатель кода. Если метод заставит переданный аргумент ссылаться на другой объект, используйте его refдля уже инициализированного объекта и outдля методов, которые должны инициализировать новый объект для переданного аргумента. Кроме того, refи outведите себя так же.

2
10.10.2019 09:52:27

Для тех, кто ищет краткий ответ.

Оба refи outключевые слова используются для передачи reference.


Переменная refключевого слова должна иметь значение или должна ссылаться на объект или null перед его передачей.


В отличие от этого ref, переменная outключевого слова должна иметь значение или должна ссылаться на объект или null после его передачи, а также не должна иметь значение или ссылаться на объект перед передачей.

3
26.11.2019 07:49:35