Почему все перегрузки TryParse имеют параметр out?

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

Я хочу проверить, является ли строка целым числом, если это целое число, то напечатайте «Целое число»; в противном случае выведите «Не целое число». Итак, вот код:

string value = Console.ReadLine(); //Get a value from the user.
int num; //Why should I have it?? No need at all !

if (int.TryParse(value, out num))
{
    Console.WriteLine("An integer");
}
else
{
    Console.WriteLine("Not an integer");
}

Мне просто интересно, почему TryParse всегда возвращает параметр out? Почему у него нет перегрузки без параметра out?


person Misha Zaslavsky    schedule 25.10.2013    source источник
comment
Не зная наверняка, я бы предположил, что вы можете сохранить значение без второго вызова Int32.Parse. В большинстве случаев я вижу, что вы хотите где-то сохранить номер.   -  person Andrew    schedule 25.10.2013
comment
Ваш вариант использования, безусловно, не является распространенным случаем. Обычно при попытке проанализировать целое число вы также хотите использовать проанализированное значение. Вот для чего нужен параметр out.   -  person Daniel Hilgarth    schedule 25.10.2013
comment
Потому что смысл в том, чтобы разобрать что-то из одного в другое...   -  person Liam    schedule 25.10.2013
comment
Очевидно, вам не понравился правильный ответ (= потому что в этом весь смысл tryParse). Ваш неявный вопрос, я думаю, заключается в том, можете ли вы избежать параметра out; и я, честно говоря, не вижу смысла даже думать об этом: это просто объявление переменной; если вы не хотите использовать его значение, не используйте его. Бессмысленный вопрос имхо.   -  person varocarbas    schedule 25.10.2013
comment
Здесь я покажу один пример, когда он не нужен. Есть еще примеры?   -  person user2586804    schedule 25.10.2013
comment
Потому что делать одно и то же дважды, один раз, чтобы посмотреть, работает ли это, и второй раз, чтобы действительно получить результат, как правило, плохо.   -  person Mat J    schedule 25.10.2013
comment
Вы получаете несколько отрицательных голосов, я думаю, это из-за того, как вы сформулировали вопрос, как будто говоря, как смеет .NET не соответствовать моим требованиям. На самом деле это не плохой вопрос.   -  person CodingIntrigue    schedule 25.10.2013
comment
Весь этот вопрос бессмысленен, с таким же успехом можно жаловаться на то, что небо голубое...   -  person Liam    schedule 25.10.2013
comment
То, что нет однозначного ответа, не означает, что сам вопрос бессмысленен. Это просто означает, что однозначного ответа нет.   -  person CodingIntrigue    schedule 25.10.2013
comment
Ни один из ответов на самом деле не объясняет, почему используется ключевое слово out, а не ref или отсутствие ключевого слова.   -  person Harrison    schedule 25.10.2013
comment
Вопрос не бессмысленный. Я тестировал просто if что-то числовое (без необходимости фактического значения впоследствии) сам более чем один раз. Если это то, что вы делаете достаточно часто, напишите оболочку (методы расширения хороши для этого) и избавьте себя от ввода нескольких дополнительных объявлений переменных.   -  person AllenG    schedule 25.10.2013
comment
@RGraham Тот факт, что однозначного ответа нет, не делает его бессмысленным, но делает его плохим вопросом для этого сайта. Если вы хотите знать, почему Microsoft разработала свой метод, спросите их, почему они это сделали. Спрашивая кучу людей, которые никоим образом не участвовали в решении, вы получите кучу догадок, а не конкретные ответы, на что этот сайт не рассчитан.   -  person Servy    schedule 25.10.2013
comment
@Servy Наверное, ты прав. Я всегда думал, что аспект, основанный на мнении, должен предотвращать дискуссии. Здесь не может быть никаких мнений или дискуссий, это твердый ответ тех, кто знает, что здесь не ответят. Что-то для меты, наверное :)   -  person CodingIntrigue    schedule 25.10.2013
comment
@RGraham Да, я согласен, что это не лучшая причина, но, на мой взгляд, это лучшее из того, что есть. Каждый опубликованный ответ в значительной степени основан на мнении. Хотя теоретически сотрудник MS может дать ответ, не основанный на мнении, его здесь нет, и все остальные дают ответы, основанные на мнении. Таким образом, хотя вопрос, технически, не требует основанных на мнении ответов, это все, что он когда-либо получит, так что это делает близкую причину достаточно подходящей в моих глазах.   -  person Servy    schedule 25.10.2013
comment
TryParse — не всегда правильный выбор. В случае, описанном выше, я бы пошел с int.parse и изменил if на try...catch   -  person Robbie Dee    schedule 04.03.2014
comment
@RobbieDee Это может быть хорошим ответом, но вопрос в том, что лучше? попробовать поймать или попробовать...?   -  person Misha Zaslavsky    schedule 05.03.2014
comment
Обработка исключений — более затратная операция. Имеет ли это значение? Ну, это зависит от того, где он находится в коде. Возможно, если это цикл или критическая по времени часть кода. Вы можете обнаружить, что раздражение от фиктивной переменной перевешивает снижение производительности или наоборот.   -  person Robbie Dee    schedule 05.03.2014
comment
Другие альтернативы будут включать использование try catch, например, @RobbieDee упомянул одну из альтернатив, а обработка исключений является дорогостоящей операцией. Другой способ может быть try { Convert.ToInt32(value); Console.WriteLine("Integer"); } catch (FormatException) { Console.WriteLine("Not an integer"); }   -  person Shishir Gupta    schedule 01.09.2017
comment
Возможно, мы никогда не узнаем причину, по которой это необходимо, но 4 года спустя вы можете сохранить строку кода, выполнив int.TryParse("1", out var _);. ссылка: blogs.msdn .microsoft.com/mazhou/2017/06/27/   -  person Ray Cheng    schedule 22.02.2018


Ответы (4)


Обновленный ответ:

В более поздних версиях C# вы можете объявить выходной параметр встроенным, что позволит вам удалить строку кода, которая вам не нужна в вашем примере:

string value = Console.ReadLine(); //Get a value from the user.

if (int.TryParse(value, out int num))
{
    Console.WriteLine("An integer");
}
else
{
    Console.WriteLine("Not an integer");
}

Вы можете просто проигнорировать результат в своем коде и больше не иметь этой лишней строки. У вас все еще есть лишний параметр, но так ли это?

Основное «почему» остается прежним и вряд ли когда-либо изменится. Метод должен был возвращать две вещи: bool, указывающий на успех, и int, указывающий результирующее значение в случае успеха. (Я не могу придумать другого способа передать результат, а вы?) Поскольку метод может возвращать только одну вещь, а пользовательский тип результата кажется излишним для этого, было принято решение вернуть bool и получить результат быть параметром out. И как только это решение было принято, оно должно оставаться в силе на протяжении всего языка.

«Они», безусловно, могут добавить перегрузку, которая не выводится в значении int. Но почему? Зачем тратить усилия на проектирование, документирование, тестирование и, как мы видели, постоянную поддержку метода, который не служит никакой цели, кроме экономии нескольких нажатий клавиш для крайнего меньшинства разработчиков? Опять же, очень маловероятно.

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


Исходный ответ:

Короткий ответ: «Потому что так определен метод». Возможно, случайно кто-нибудь из языковой группы C# найдет этот вопрос и объяснит, почему были приняты решения, но на данный момент это мало что меняет. C# — это статически компилируемый язык, и сигнатуры методов должны совпадать, так что это именно так.

(Представьте, если бы они изменили это и сломали .TryParse() во всех существующих кодовых базах. Это было бы... плохо.)

Однако вы можете обойти это в своем собственном коде. Что-то столь же простое, как метод расширения, может помочь вам:

public static bool IsInt(this string s)
{
    int x = 0;
    return int.TryParse(s, out x);
}

Затем в вашем коде вам просто нужно вызвать этот метод из строкового значения:

string value = Console.ReadLine();
if (value.IsInt())
    Console.WriteLine("An integer");
else
    Console.WriteLine("Not an integer");
person David    schedule 25.10.2013
comment
Но приводит разработчика к искушению использовать его всегда, даже если он все равно планирует использовать int.Parse. - person Tim Schmelter; 25.10.2013
comment
@TimSchmelter: Это действительно так. Хотя нет недостатка в неправильных и неэффективных вещах, которые разработчик может сделать в коде в любом случае :) В примере с ОП это не проблема, но в других ситуациях пробег может отличаться. - person David; 25.10.2013
comment
@David Вы только что сделали расширение, а out int все еще используется, я не уверен, что насчет производительности, но я думаю, что это немного снижает производительность. Более разумно сделать расширение для int, но проблема в том, что вы должны написать всю реализацию .net для TryParse, но без параметра out. - person Misha Zaslavsky; 25.10.2013
comment
@MishaZaslavsky: параметр out все еще используется, потому что он все еще нужен int.TryParse(). Это абстрагирует его за методом, когда он не нужен вашему коду. Как указывает Тим, все равно следует избегать использования этого расширения в сочетании с int.Parse(), так как это удваивает усилия по анализу целого числа. (См. его ответ для примера.) Альтернативой здесь является полностью написать собственную версию .TryParse(), которая, вероятно, не принесет высокой отдачи от инвестиций. По крайней мере, в этой версии ненужный для домена параметр out абстрагируется за более подходящим для домена интерфейсом. - person David; 25.10.2013
comment
@David Насчет ответа Тима, это правда, но я могу привести много примеров, в которых неопытные могут ошибаться, это не повод убирать опцию для тех, кому она действительно нужна. Я думаю, что лучший способ - это когда визуальная студия добавит перегрузку без параметра out :) - person Misha Zaslavsky; 25.10.2013
comment
@MishaZaslavsky: Ну, я бы не стал на это рассчитывать. (И это не Visual Studio, это .NET Framework в целом.) За исключением изменений в System API, следующий лучший способ — абстрагировать его за предметно-ориентированными помощниками, такими как этот. - person David; 25.10.2013
comment
@MishaZaslavsky Я понимаю, что это очень старый вопрос, однако в C# 7 они добавили функциональность, позволяющую отбрасывать вашу выходную переменную, используя следующий синтаксис: int.TryParse(myStr, out _). Очевидно, что переменная out по-прежнему существует, но они явно сделали это для представления синтаксического анализа, результат которого вас не волнует. Это проясняет код и немного короче, чем out var num). - person GoonPontoon; 27.12.2019

TryParse — относительно сложная операция для определения int представления string. Если бы была перегрузка, которая просто возвращает bool, очень вероятно, что многие (неопытные) разработчики будут следовать этому неэффективному шаблону:

int myInt = -1;
if(int.TryParse("123"))
    myInt = int.Parse("123");
person Tim Schmelter    schedule 25.10.2013
comment
Пока производительность не является вашей главной задачей (а очень часто это не так), я не вижу здесь никаких проблем. Без параметра out вы не обязаны вводить именованную локальную переменную, поэтому вы можете напрямую передать результат другому методу. Во многих случаях это может сделать ваш код более читабельным. - person lex82; 25.11.2014
comment
@lex82: но int.Parse все равно нужно вычислить параметр int. Так почему же он должен выбрасывать эту информацию? Звонящий должен сам решить, использовать его или нет. Вы даже можете повторно использовать эту локальную переменную, например, в запросе LINQ. Однако вы всегда можете написать расширение, как показано выше. - person Tim Schmelter; 25.11.2014
comment
Думаю, это дело вкуса. Моя проблема только в том, что код замусорен объявлениями переменных - обычно вне операторов if, которые используют значение только в одной ветке, если вообще используют. Я бы предпочел потратить несколько циклов ЦП ради удобочитаемости. Представьте, что вы не можете вызвать HasValue для поднятых типов без объявления и передачи параметра out. - person lex82; 25.11.2014
comment
@lex82: я бы объявил переменные там, где я их использую. programmers.stackexchange.com/questions/56585/ (не по указанной там причине, а по причине, которую вы упомянули, здесь лучший пример). Однако я думаю, что знаю, что вы имеете в виду, иногда мне также нравится удалять локальную переменную и использовать расширение, подобное это. - person Tim Schmelter; 25.11.2014

У него есть параметр out, потому что в подавляющем большинстве случаев, когда люди его используют, им нужен int (или двойной, или десятичный, или datetime, или что-то еще), который был проанализирован.

Если вам не нужно/не нужно анализируемое значение, и вы делаете это все время, вы можете написать свою собственную «оболочку» для .TryParse(), которая просто берет строку.

В этом примере (и вы могли бы сделать его более общим, я уверен) вы могли бы сделать что-то вроде

public static bool TryParseNoOutput(this string value)
{
  int noNeed = 0;
  return int.TryParse(value, out noNeed);
}

Затем в вашем коде (в данном случае) вы бы назвали:

string value = Console.ReadLine();
if(value.TryParseNoOutput()) Console.WriteLine("An Integer");
else Console.WriteLine("Not an integer."):

Редактировать: Как было указано в комментариях, я пытался вызвать "int.TryParseNoOutput", когда я определил его как расширение строки. Ответ был обновлен, чтобы отразить это.

person AllenG    schedule 25.10.2013
comment
Круто, как ты определил новый static на int? - person user2586804; 25.10.2013
comment
Как, возможно, пытается указать @user, методы расширения работают с экземпляром, а в вашем случае - с экземпляром строки. - person CodeCaster; 25.10.2013
comment
@CodeCaster да, мне было немного весело, я знал, что он не мог так определить это. Но это в миллионе миль от value.TryParseNoOutput() - person user2586804; 25.10.2013
comment
Упс. Ты прав. Я исправлю это прямо сейчас... - person AllenG; 25.10.2013
comment
Могу ли я предложить альтернативное имя, которое лучше отражает то, что тестируется? value.IsInteger() - person user2586804; 25.10.2013
comment
Или value.CanParseInt(). Потому что как есть, он больше не говорит, что он пытается разобрать как. Пытается ли он анализировать как int, IPAddress, float или DateTime? Кто может сказать. - person user2586804; 25.10.2013
comment
Да, я мог бы изменить имя, но я полагаю, что ОП может сам это понять. Честно говоря, я бы вообще не стал этого делать, поскольку объявление int на самом деле не является достаточным усилием, чтобы оправдать метод расширения. Я просто показывал, как это можно сделать. - person AllenG; 25.10.2013

Почему в TryParse есть выходной параметр?
По очень простой причине реализации TryParse.
Способ, которым вы определяете, можно ли что-то анализировать, заключается в анализе этого! Если вы можете что-то разобрать, то это можно разобрать. Если вы не можете разобрать его, то он не поддается разбору.

Таким образом, определяя, парсится что-то или нет, если это можно парсить, значит, мы это уже разобрали! Было бы глупо отбрасывать это проанализированное значение (любой, кто задается вопросом, можно ли что-то разобрать, скорее всего, заинтересован в проанализированном результате), поэтому проанализированное значение возвращается.

У него есть выходной параметр, поскольку анализируемое значение является побочным продуктом возвращающего истину вызова TryParse.

person Task    schedule 25.10.2013