Зачем использовать ключевое слово out вместо присваивания в С#?

Я исследовал ключевое слово out в C# после прочтения раздела об этом в C# in Depth. Кажется, я не могу найти пример, показывающий, почему ключевое слово требуется вместо простого присвоения значения оператору возврата. Например:

public void Function1(int input, out int output)
{
    output = input * 5;
}

public int Function2(int input)
{
    return input * 5;
}

...
int i;
int j;

Function1(5, out i);
j = Function2(5);

И i, и j теперь имеют одинаковое значение. Это просто удобство возможности инициализации без знака = или есть какое-то другое значение, которое я не вижу? Я видел несколько похожих ответов, в которых упоминалось, что он перекладывает ответственность за инициализацию на вызываемого здесь. Но все это вместо того, чтобы просто присвоить возвращаемое значение и не иметь сигнатуры метода void?


person Matt Phillips    schedule 02.05.2011    source источник
comment
Я нахожу это несколько забавным, что почти все придумали один и тот же пример для чего-то, что имеет выходной параметр   -  person Matti Virkkunen    schedule 03.05.2011
comment
Связанный вопрос... stackoverflow.com/questions/413218/   -  person marcoaoteixeira    schedule 03.05.2011


Ответы (7)


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

Хорошим примером является Int32.TryParse(input, out myVar), он вернет true, если он был успешным, и false в противном случае. Вы можете получить преобразованный int через параметр out.

int myOutVar;

if (Int32.TryParse("2", out myOutVar))
{
   //do something with the int
}else{
    //Parsing failed, show a message
}
person Chris Kooken    schedule 02.05.2011
comment
Другими словами, out используется для возврата нескольких значений без создания отдельного объекта для их хранения, что более эффективно, а иногда и удобнее. - person Matti Virkkunen; 03.05.2011
comment
Это также более читабельно, как это. В случае Int32.TryParse не имеет смысла возвращать int для преобразования, потому что вам нужно, если преобразование завершилось неудачей или нет. Разумнее вернуть true или false (более читабельно), что преобразование прошло и если да, то получить преобразованный int. - person Xaisoft; 03.05.2011
comment
Смотрите мой комментарий к ответу @taylonr. Я понимаю, что это значит, теперь я думаю, что я просто в лагере, что лучше обрабатывать результат с исключением... - person Matt Phillips; 03.05.2011
comment
Исключения полностью раскручивают стек вызовов и не очень эффективны. Это гораздо более эффективный способ сделать это. Рекомендуется избегать выполнения логики с блоками try/catch. - person Chris Kooken; 03.05.2011
comment
@Xaisoft: Хотя некоторым людям нравится, когда методы TryXX возвращают логическое значение «ok» и сохраняют результат в параметре «out», я бы предположил, что использование параметра «out» для логического значения при возврате результата часто может быть чище. Среди прочего, (1) типы возвращаемых функций в универсальных интерфейсах поддерживают ковариантность, но параметры «out» не могут; (2) часто было бы лучше сказать Boolean ok; var что угодно = что-то.TryXXX(ok); если (хорошо) useValue (что угодно); чем что угодноТип что угодно; если (TryXXX(что угодно)) useValue(что угодно); - person supercat; 01.09.2011

Ключевые слова out / ref в C# следует использовать только тогда, когда вам нужно вернуть несколько значений. Даже в этом случае вы должны сначала рассмотреть возможность использования типа контейнера (например, Tuple) для возврата нескольких значений, прежде чем вернуться к out / ref. Всякий раз, когда вы возвращаете одно значение, оно должно быть просто возвращено.

person JaredPar    schedule 02.05.2011

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

Рассмотрим метод TryGetValue для IDictionary (скажем, myDictionary является IDictionary<string, string>) Вместо того, чтобы делать это:

string value = String.Empty;
if (myDictionary.ContainsKey("foo"))
{
  value = myDictionary["foo"];
}

И ContainsKey, и индексатор должны искать ключ в словаре, но вы можете избежать этого двойного поиска в положительном случае, выполнив:

string value;
if (!myDictionary.TryGetValue("foo", out value))
{
  value = String.Empty;
}

ИМО, это достойная причина для использования out параметров.

person rusty    schedule 02.05.2011

К сожалению, мы не можем сделать что-то подобное на C#:

a,b = func(x,y,z);

то, что мы делаем на Python или других языках. вроде преодолевает это.

Я считаю, что F # преодолел это с помощью кортежей.

PS: возврат нескольких значений из функции не всегда может быть хорошим. Крошечные типы хороши в большинстве случаев — http://www.martinfowler.com/bliki/DataClump.html

person manojlds    schedule 02.05.2011
comment
После прочтения первоначальных ответов это было именно то, о чем я подумал. Очень интересно, что ни один из ресурсов (включая книгу) так не выразился. По сути, это способ разрешить несколько возвращаемых значений без необходимости помещать их в объект передачи? - person Matt Phillips; 03.05.2011

Например, Int32.TryParse возвращает логическое значение, если оно проанализировано правильно и с параметром out изменяет значение. Если проанализированное значение равно 0 и оно возвращает true, это означает, что значение, которое вы отправили для анализа, было 0. Если он возвращает false, то синтаксический анализатор не работает.

person BrunoLM    schedule 02.05.2011

Некоторые из них для ясности. Возьмите методы TryParse(), например

Int32.TryParse("3", out myInt);

Это возвращает логическое значение, указывающее, можно ли преобразовать строку в целое число. Если бы вы только что

Int32.TryParse("3", myInt);

Что происходит, когда это называется? Назначен ли myInt? Возвращает ли TryParse целое число?

Это не очевидно. Но если у меня есть параметр out, то я знаю, что значение присваивается, а возвращается что-то другое.

person taylonr    schedule 02.05.2011
comment
будет ли это преимуществом перед простым исключением? Если вы пытаетесь проанализировать int, и это не удается, потому что это строка, вы не должны нести ответственность за это. Я считаю, что обнаружение и исправление ошибок лучше оставить блоку try catch, чем оператору if/else. - person Matt Phillips; 03.05.2011
comment
@Matt, это зависит от того, является ли недопустимый формат исключительным случаем (в этом случае создание исключения является правильным), и вам следует использовать int.Parse, но в случае, когда вы можете ожидать недопустимый формат (например, ввод пользователя), вы должны использовать int.TryParse - person Rune FS; 03.05.2011
comment
@ Мэтт, как говорит Рун, это зависит от случая. Но в любом случае .TryParse() не совсем ясен в своих намерениях.. это почти как те логические перечисления, которые вы видите, что у кого-то есть False = 0, True = 1, Unknown = 2 - person taylonr; 03.05.2011

В основном вы делаете что-то вроде (моя база данных читается)

if (ReadSingle<UserRecord>(cmd, out user))
    Cache.Insert(cacheId, user, null,
        DateTime.MaxValue, TimeSpan.FromMinutes(3));

Или же вы делаете что-то вроде:

user = ReadSingle<UserRecord>(cmd);
if(null != user)
   // Cache.Insert ...

Это немного упрощает код, чтобы использовать логический результат (что запись была прочитана из базы данных) и получить фактическую запись в переменную с помощью ключевого слова out.

person Chuck Savage    schedule 02.05.2011
comment
отличный пример. Я считаю, что трудно придумать нетривиальные примеры вещей, которые показывают что-то большее, чем простое или крутое. Спасибо - person Matt Phillips; 03.05.2011
comment
Добро пожаловать. Мне потребовалось некоторое время, чтобы адаптировать этот стиль вызовов метода Try, но благодаря этому код выглядит немного лучше. - person Chuck Savage; 03.05.2011