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

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

public void myFunction(ref MyClass someClass)

а также

public void myFunction(out MyClass someClass)

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


person TK.    schedule 23.12.2008    source источник
comment
Вы: Мне нужно передать объект, чтобы его можно было изменить Похоже, MyClass будет class типом, то есть ссылочным типом. В этом случае переданный вами объект может быть изменен с помощью myFunction даже без ключевого слова _4 _ / _ 5_. myFunction получит новую ссылку, указывающую на тот же объект, и сможет изменять этот же объект сколько угодно. Отличие ключевого слова ref будет заключаться в том, что myFunction получит такую ​​же ссылку на тот же объект. Это было бы важно только в том случае, если бы myFunction изменил ссылку на другой объект.   -  person Jeppe Stig Nielsen    schedule 08.06.2013
comment
Меня озадачивает количество запутанных ответов здесь, когда @ AnthonyKolesov совершенно идеален.   -  person o0'.    schedule 24.12.2013
comment
Объявление метода out полезно, когда вы хотите, чтобы метод возвращал несколько значений. Один аргумент можно присвоить null. Это позволяет методам опционально возвращать значения.   -  person Shin Kazama    schedule 19.08.2014
comment
Здесь объяснено на примере. Это более понятно :) dotnet-tricks.com/Tutorial/csharp/   -  person Prageeth godage    schedule 08.03.2016
comment
Комментарий @ JeppeStigNielsen технически является (единственным) правильным ответом на фактический вопрос OP. Чтобы передать объект в метод, чтобы метод мог изменять объект, просто передайте объект (ссылку на) в метод по значению. Изменение объекта в методе с помощью аргумента объекта изменяет исходный объект, даже если метод содержит свою собственную отдельную переменную (которая ссылается на тот же объект).   -  person David R Tribble    schedule 30.06.2018


Ответы (27)


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

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

person Rune Grimstad    schedule 23.12.2008
comment
Еще одна интересная особенность out - это то, что функция должна назначаться параметру out. Нельзя оставлять его неназначенным. - person Daniel Earwicker; 23.12.2008
comment
"ref" применимо только к типу значения? Поскольку ссылочный тип всегда передается по ссылке. - person faulty; 24.12.2008
comment
да. Типы значений, включая структуры - person Rune Grimstad; 31.12.2008
comment
@faulty: Нет, ссылка применима не только к типам значений. ref / out похожи на указатели в C / C ++, они имеют дело с местом в памяти объекта (косвенно в C #) вместо прямого объекта. - person thr; 15.03.2010
comment
@faulty: Как ни странно, ссылочные типы всегда передаются по значению в C #, если вы не используете спецификатор ref. Если вы установите myval = somenewval, эффект будет только в области этой функции. Ключевое слово ref позволит вам изменить myval, чтобы он указывал на какое-то новое значение. - person JasonTrue; 15.04.2010
comment
@DanielEarwicker: Для функций, реализованных на C #, компилятор будет настаивать на том, чтобы out параметры были записаны во всех неисключительных путях кода; однако это не применяется для функций, включая переопределения виртуальных функций, реализованных на других языках. Например, вполне возможно, что реализация IDictionary<TK,TV>.TryGetValue(TK, out TV) оставит свой параметр out неизменным, если значение не будет найдено. - person supercat; 21.02.2015
comment
@faulty Вам действительно стоит проверить другой вопрос, описывающий ref, используемый со ссылочными типами ;-). - person binki; 18.10.2016
comment
Есть трюк, как перейти к неинициализированному объекту ref без каких-либо ошибок времени компиляции, см. Мой ответ - person chviLadislav; 19.01.2017
comment
И ref, и out нельзя использовать при перегрузке метода одновременно. Однако во время выполнения ref и out обрабатываются по-разному, но во время компиляции они обрабатываются одинаково. CLR не делает различий между ними в IL. - person Bharathkumar V; 17.04.2018
comment
@BharathkumarV Разве ваш комментарий не обратный? ref и out обрабатываются по-разному во время компиляции, но обрабатываются одинаково во время выполнения. - person NetMage; 16.02.2021

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

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

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

  1. Значение не установлено и не может быть прочитано методом , пока не установлено.
  2. Метод должен установить его перед возвратом.
person Anton Kolesov    schedule 23.12.2008
comment
Этот ответ наиболее четко и кратко объясняет ограничения, которые компилятор налагает при использовании ключевого слова out, а не ключевого слова ref. - person Dr. Wily's Apprentice; 02.09.2010
comment
Из MSDN: параметр ref должен быть инициализирован перед использованием, в то время как параметр out не должен быть явно инициализирован перед передачей, и любое предыдущее значение игнорируется. - person Shiva Kumar; 20.08.2013
comment
С out можно ли вообще прочитать его в методе до того, как он был установлен этим методом, если он был инициализирован до вызова метода? Я имею в виду, может ли вызываемый метод читать то, что вызывающий метод передал ему в качестве аргумента? - person Panzercrisis; 25.03.2016
comment
Panzercrisis, например, вызываемый метод может читать, если он уже установлен. но он должен установить его снова. - person robert jebakumar2; 30.04.2017

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

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

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

person Michael Blackburn    schedule 23.03.2010
comment
ref Дом написал бы отчет карандашом, чтобы Питер мог его изменить. - person Deebster; 05.08.2011
comment
@Deebster, ты знаешь, эта метафора ничего тебе не сделала, почему ты должен ее так мучить? ;) - person Michael Blackburn; 08.08.2011
comment
занимательный, но обучающий, stackoverflow нужно больше таких сообщений - person Frank Visaggio; 26.06.2012
comment
На случай, если кто-то сочтет этот ответ полусмешным, посмотрите фильм «Офисное пространство». - person displayName; 13.06.2017
comment
а Дом и босс Питерса будут стоять за Домом (в качестве аргумента), заставляя обоих поработать над распечаткой заново, пока Питер не передаст Домду распечатку. - person Patrick Artner; 21.12.2017
comment
Отличное объяснение. Это очень полезно для студентов, которым сложно понять основные концепции C #. Так держать :-) - person Kushan Randima; 27.09.2020

Попробую объяснить:

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

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

Создадим ссылочный тип:

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

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

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

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

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

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

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

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

(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
person James Roland    schedule 23.09.2010
comment
Вы имеете в виду, что в конце концов (в случае ref) есть только одна ссылка на данные, но для них два псевдонима. Верно? - person Sadiq; 14.02.2015
comment
Проголосовали за четкое объяснение. Но я думаю, что это не отвечает на вопрос, поскольку не объясняет разницу между параметрами ref и out. - person Joyce Babu; 01.09.2016
comment
Удивительный. Вы можете объяснить то же самое, что и для ключевого слова out? - person Asif Mushtaq; 27.09.2016

ссылка находится в и нет .

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

person Ruben Bartelink    schedule 23.12.2008
comment
не совсем так, как принятый ответ ref, если направлен и бесполезно игнорировать типы значений, если не передается обратно. - person kenny; 27.03.2010
comment
@kenny: Не могли бы вы немного уточнить, например, какие слова вы бы изменили, чтобы сохранить дух ответа, но убрать неточность, которую вы воспринимаете? Мой ответ не является сумасшедшим предположением новичка, но поспешность (краткость, опечатки) в вашем комментарии, кажется, предполагает, что это так. Цель состоит в том, чтобы с помощью наименьшего количества слов дать представление о различиях. - person Ruben Bartelink; 27.03.2010
comment
(Кстати, я знаком с типами значений, ссылочными типами, передачей по ссылке, передачей по значению, COM и C ++, если вам будет полезно сослаться на эти концепции в своем пояснении) - person Ruben Bartelink; 27.03.2010
comment
Я не возражаю, но только мелкая нить с «ref» только для ввода. У меня 2 очка. 1 объекты всегда передаются по ссылке. И второй момент заключается в том, что вы добавляете ссылку только в том случае, если вас волнует, что происходит внутри метода или у него есть побочные эффекты для вызывающего. Я думаю, что, возможно, я неправильно истолковал ваш ответ в контексте вопроса, я прочитал его, так как «ref» означает «in», а «out» - «out». - person kenny; 27.03.2010
comment
Ссылки на объекты передаются по значению (кроме случаев использования ключевого слова ref или out). Думайте об объектах как об идентификационных номерах. Если переменная класса содержит объект № 1943, и эта переменная передается по значению в подпрограмму, эта подпрограмма может вносить изменения в объект № 1943, но не может указывать на что-либо иное, кроме объекта № 1943. Если переменная была передана по ссылке, процедура могла бы заставить точку переменной содержать объект № 5441. - person supercat; 15.01.2012
comment
@supercat: Если вы согласны (не видите +1) и хотите объяснить @ kenny, вам нужно @ его войти. Если вы хотите объяснить мне разницу между ссылками и значениями, вы только что это сделали ... Если вы считаете, что этот ответ неверен и нуждается в переписывании на основе вашего комментария, не стесняйтесь редактировать и / или добавлять свой собственный ответ. - person Ruben Bartelink; 16.01.2012
comment
@RubenBartelink: Спасибо за предупреждение; Я хотел известить Кенни. Я бы хотел, чтобы описание идентификатора объекта использовалось более широко; Я думаю, это проясняет ситуацию. Если рабочему кузовного цеха передают лист бумаги с номером лицензии и просят покрасить эту машину в синий цвет, можно ожидать, что он найдет машину, права которой совпадают с лицензией на бумаге, и раскрасит ее, оставив номер только на бумаге. . От него не ожидали, что он раскрасит бумагу синим цветом или перекрасит ее синими буквами. - person supercat; 16.01.2012
comment
@supercat: Мне нравится ваше объяснение ref vs val (и этой последующей анаологии). Я думаю, что Кенни на самом деле не нужно объяснять ему что-либо из этого, хотя (относительно) запутанные его комментарии были. Я действительно хотел бы, чтобы мы все могли просто удалить эти проклятые комментарии, поскольку они просто сбивают всех с толку. Основная причина всей этой чепухи, похоже, в том, что Кенни неправильно прочитал мой ответ и еще не указал ни единого слова, которое следует добавить / удалить / заменить. Никто из нас троих ничего не извлек из обсуждения, о котором мы еще не знали, а другой ответ получил смехотворное количество голосов. - person Ruben Bartelink; 16.01.2012

из:

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

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

ссылка:

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

  1. Перед вызовом функции вам необходимо инициализировать переменную.
  2. Необязательно присваивать какое-либо значение параметру ref в методе. Если вы не меняете значение, зачем помечать его как «ref»?
person Nazmul Hasan    schedule 01.04.2016
comment
В C # метод может возвращать только одно значение. Если вы хотите вернуть более одного значения, вы можете использовать ключевое слово out. Мы также можем использовать ref для возврата значения. Значит, мы можем использовать как ref, так и out, если хотим вернуть несколько значений из метода? - person Ned; 23.05.2018
comment
В C # 7 вы можете возвращать несколько значений с помощью ValueTuples. - person Iman Bahrampour; 09.12.2018

Расширение собаки, пример кота. Второй метод с 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;
    }
person BBB    schedule 19.05.2010

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

  • ref переменная должна быть инициализирована перед использованием. out переменную можно использовать без присваивания
  • Параметр out должен рассматриваться функцией, которая его использует, как неназначенное значение. Итак, мы можем использовать инициализированный параметр out в вызывающем коде, но значение будет потеряно при выполнении функции.
person gmail user    schedule 10.02.2014

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

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

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

person 2upmedia    schedule 25.12.2015

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

person Albic    schedule 23.12.2008
comment
Это работает, только если новый объект не создается и не возвращается. При создании нового объекта ссылка на старый объект будет потеряна. - person etsuba; 23.12.2008
comment
Это неправильно - попробуйте следующее: добавить someObject = null в Bar конец выполнения. Ваш код будет работать нормально, поскольку только ссылка Bar на экземпляр была обнулена. Теперь измените Bar на Bar(ref MyClass someObject) и выполните снова - вы получите NullReferenceException, потому что ссылка Foo на экземпляр тоже была обнулена. - person Keith; 27.09.2010

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


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

person Farhan S.    schedule 06.12.2014

"Бейкер"

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

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

string s = "Able";

тогда s - это ссылка на строковый класс, содержащий текст «Able»! Другое присвоение той же переменной через

s = "Baker";

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

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

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

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

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

person mmmmmmmm    schedule 23.12.2008
comment
Точно. Так что не совсем верно сказать, что поскольку вы передаете ссылочный тип (класс), нет необходимости использовать ref. - person Paul Mitchell; 23.12.2008
comment
Теоретически это правильно, потому что он написал так, что это можно изменить, что невозможно на строках. Но из-за неизменности объектов ref и out очень полезны также для ссылочных типов! (.Net содержит множество неизменяемых классов!) - person mmmmmmmm; 23.12.2008
comment
Да, ты прав. Я не думал о неизменяемых объектах, таких как строки, потому что большинство объектов изменчивы. - person Albic; 23.12.2008
comment
Что ж, это, конечно, загадочный ответ, который стоит увидеть в LQP; в этом нет ничего плохого, за исключением того, что это кажется длинным и подробным ответом на другой комментарий (поскольку исходный вопрос не упоминает Эйбла и Бейкера ни в одной из его редакций), как если бы это был форум. Я предполагаю, что это еще не было решено еще тогда, когда. - person Nathan Tuggy; 25.05.2015

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();

      }
   }
}
person Faisal Naseer    schedule 25.10.2014

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

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


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


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

person snr    schedule 26.11.2019

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

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

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

        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);
person RotatingWheel    schedule 15.03.2015
comment
Вы можете объявить переменную inline: out double Half_nbr. - person Sebastian Hofmann; 08.02.2018

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

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

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

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

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

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

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

(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) Среда выполнения запускает процедуру вызова для вызываемого метода и как ее часть проверяет тип параметров.

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

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

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

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


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

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

person supi    schedule 29.05.2017

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

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

    • The calling method will see changes like someClass.Message = "Hello World" whether you use ref, out or nothing
    • Запись someClass = new MyClass() внутри myFunction(someClass) заменяет объект, видимый someClass, только в рамках метода myFunction. Вызывающий метод все еще знает об исходном экземпляре MyClass, который он создал и передал вашему методу.
  • Вам понадобится ref или out, если вы планируете заменить someClass полностью новым объектом и хотите, чтобы вызывающий метод видел ваши изменения.

    • Writing someClass = new MyClass() inside myFunction(out someClass) changes the object seen by the method that called 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 на что-то другое»

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

public void PoorlyNamedMethod(out SomeClass x)

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

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

Что ж, это была пустая трата времени - потратить все эти часы на то, чтобы получить идеальный урок. Его определенно выбросят и заменят на PoorlyNamedMethod.

person Caius Jard    schedule 14.08.2019

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

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 ведут себя одинаково.

person Dietrich Baumgarten    schedule 10.10.2019

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

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

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

person Talha Khan    schedule 20.06.2013

Ссылка: ключевое слово 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).

person Dejan Ciev    schedule 26.07.2015

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

В приведенном ниже примере, когда я комментирую строку // myRefObj = new myClass {Name = "ref outside called !!"};, выдается сообщение об ошибке: «Использование неназначенной локальной переменной '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; }
} 
person Ankur Bhutani    schedule 25.06.2013

 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 / string

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

person Haris Zia    schedule 13.11.2014

С точки зрения метода, который получает параметр, разница между 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.

person supercat    schedule 11.02.2015

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

person Rakeshkumar Das    schedule 05.12.2016

Есть два основных отличия, которые я хочу привести для примера:

  1. ref и out передаются по ссылке, hense;
 class Program
    {
        public static void Main(string[] args)
        {
            var original = new ObjectWithMememberList(3);
            Console.WriteLine(original.MyList.Capacity); // 3
            ChangeList(original.MyList);
            Console.WriteLine(original.MyList.Capacity); // 3
        }

        static void ChangeList(List<int> vr)
        {
            vr = new List<int>(2);
        }
}

но:

 class Program
    {
        public static void Main(string[] args)
        {
            var original = new ObjectWithMememberList(3);
            Console.WriteLine(original.MyList.Capacity); // 3
            ChangeList(ref original.MyList);
            Console.WriteLine(original.MyList.Capacity); // 2
        }

        static void ChangeList(ref List<int> vr)
        {
            vr = new List<int>(2);
        }
}

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

ChangeList(ref new List<int>()); // Compile Error [might not be initialized before accessing]

но:

List<int> xs;
ChangeList(out xs); // Compiles
person Zvi Mints    schedule 29.05.2021

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

Например,

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

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

person Mangesh Pimpalkar    schedule 14.04.2010
comment
Хотя здесь все в значительной степени верно, на самом деле это не объясняет разницу между значением по ссылке или выходом. В лучшем случае это наполовину объясняет разницу между ссылочными и значениями / неизменяемыми типами. - person Conrad Frix; 25.12.2012
comment
Если вы хотите, чтобы этот код писал cat, передайте этот объект вместе с ключом ref следующим образом: public static void Bar (ref MyClass someObject), Bar (ref myObject); - person Daniel Botero Correa; 04.02.2019

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

        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 только для типов структур / значений и самой строки, поскольку строка является ссылочным типом, который притворяется, но не является типом значения.

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

person Edwin    schedule 08.12.2011
comment
Добро пожаловать в Stack Overflow, Эдвин. Насколько мне известно, строки передаются по ссылке, как и любой другой объект. Вы можете быть сбиты с толку, потому что строки являются неизменяемыми объектами, поэтому не так очевидно, что они передаются по ссылке. Представьте, что у этой строки есть метод с именем Capitalize(), который изменяет содержимое строки на заглавные буквы. Если вы затем заменили строку a = "testing"; на a.Capitalize();, то результатом будет HELLO, а не Hello. Одним из преимуществ неизменяемых типов является то, что вы можете передавать ссылки и не беспокоиться о том, что другой код изменит значение. - person Don Kirkby; 08.12.2011
comment
Существует три основных типа семантики, которые может предоставлять тип: семантика изменяемых ссылок, семантика изменяемых значений и неизменяемая семантика. Рассмотрим переменные x и y типа T, у которого есть поле или свойство m, и предположим, что x скопирован в y. Если T имеет ссылочную семантику, изменения x.m будут наблюдаться y.m. Если T имеет семантику значений, можно изменить x.m, не влияя на y.m. Если T имеет неизменяемую семантику, ни x.m, ни y.m никогда не изменятся. Неизменяемая семантика может моделироваться объектами ссылки или значения. Строки - это неизменяемые ссылочные объекты. - person supercat; 15.01.2012