У меня есть несколько подобных методов, например, например. CalculatePoint (...) и CalculateListOfPoints (...). Иногда они могут не сработать, и об этом необходимо сообщить вызывающему абоненту. Для CalculateListOfPoints, который возвращает общий список, я мог бы вернуть пустой список и потребовать от вызывающей стороны проверить это; однако Point является типом значения, поэтому я не могу вернуть там null.
В идеале мне хотелось бы, чтобы методы «выглядели» одинаково; одним из решений может быть определение их как
public Point CalculatePoint(... out Boolean boSuccess);
public List<Point> CalculateListOfPoints(... out Boolean boSuccess);
или как вариант вернуть Поинт? для CalculatePoint и вернуть значение null, чтобы указать на сбой. Это означало бы возврат к типу, не допускающему значения NULL, что кажется чрезмерным.
Другой путь - вернуть логическое значение boSuccess, получить результат (Point или List) в качестве параметра out и называть их TryToCalculatePoint или что-то в этом роде ...
Какая лучшая практика?
Обновлено: я не хочу использовать исключения для управления потоком! Иногда ожидается неудача.
Метод, который не завершается из-за параметра вне диапазона, не является «управлением потоком», а является неожиданным и исключительным условием.
@Will: Я согласен, это то, что я имел в виду под пустым списком. @Rich: Согласен; но параметры могут быть в порядке, просто может не быть подходящей точки для возврата.
@Joel: Тогда вам может понадобиться шаблон try, однако для того, что вы указали в вопросе, вы должны создать исключение.





Я бы сказал, что лучшая практика - возвращаемое значение означает успех, а исключение означает неудачу.
В приведенных вами примерах я не вижу причин, по которым вы не должны использовать исключения в случае сбоя.
Исключения не следует использовать в неисключительных случаях. Здесь нужно быть осторожным.
Я не сторонник использования исключений для «нормального» поведения (если вы ожидаете, что функция не будет работать для некоторых записей).
@Kevin: исключительный случай, например, невозможность завершить метод?
@lucasbfr: Пожалуйста, объясните, где вы считаете отказ этого метода «нормальным» поведением. Я вообще не вижу этой болтовни в вопросе.
А как насчет игр, серверов и графических приложений? Его пример свидетельствует как минимум об игре или графическом приложении. Исключения бывают медленными: очень и очень медленными. Всегда давайте потенциальным разработчикам возможность избежать исключений.
Rich B - если такой сбой ожидается, то я бы не стал считать это исключительным случаем.
@Kevin: TryParse - это совершенно другая тема, чем исключения, и это сделано специально. Если у вас есть метод, и он принимает ввод, и этот ввод приводит к состоянию, при котором метод не может возвращаться ожидаемым образом, должно быть сгенерировано исключение. Конец истории.
@Jonathon: Это смешно. Исключение не будет использоваться для управления потоком. Нет никаких причин, по которым он не должен быть обязан проверять свой ввод в первую очередь. Но чтобы не использовать исключения из-за того, что они находятся в игре или на сервере? Это смешно.
См. Редактирование: это, конечно, не подходящая ситуация для исключения.
И сбой не имеет ничего общего с границами или грязным вводом; это действительно графическое приложение, и метод не работает, если нет точки, которая удовлетворяет набору сложных условий
@Joel: Тогда попытка может быть вашим лучшим решением, однако я думаю (как ответил другой человек) вы можете захотеть реорганизовать эту часть вашего кода. Я думаю, вы ошибаетесь.
@Rich: Почему неправильный подход? (это настоящий вопрос!) Как вы посоветуете провести рефакторинг?
@Rich B: Прочтите эти ссылки: stackoverflow.com/questions/77127/when-to-throw-an-exceptionresearch.att.com/~bs/bs_faq2.html#exceptions
@ Джоэл и Кевин: Я знаю, где использовать исключения. Если бы ваш код был правильно структурирован, это не было бы проблемой. Каждый раз, когда вы находитесь в ситуации, когда вы думаете, что возврат кода ошибки может быть полезным, с вашим кодом что-то не так.
Лично я думаю, что использовал бы ту же идею, что и TryParse (): использовать параметр out для вывода реального значения и возвращать логическое значение, указывающее, был ли вызов успешным или нет.
public bool CalculatePoint(... out Point result);
Я не сторонник использования исключений для «нормального» поведения (если вы ожидаете, что функция не будет работать для некоторых записей).
@lucasbfr: +1, если вы предоставляете DoX, который может вызвать исключение, которое вы можете выяснить, предоставьте TryX.
@sixlettervariables: я согласен, я не хотел менять имя метода, потому что он не спрашивал, но это было бы очень хорошей практикой
Хотя концепция TryParse иногда бывает утомительной, потому что вам всегда нужно объявлять переменную, я привык к ней и всегда предпочитаю ее версии с генерацией исключений. Однако всякий раз, когда вы можете просто вернуть null, я делаю это вместо подхода TryParse.
Если вы собираетесь это сделать, я бы изменил имя метода на Попробовать вычислить точку.
Другой альтернативой является создание исключения. Однако обычно вы хотите создавать исключения только в «исключительных случаях».
Если случаи отказа являются обычными (а не исключительными), то вы уже перечислили два варианта. РЕДАКТИРОВАТЬ: В вашем проекте может быть соглашение о том, как обрабатывать такие неисключительные случаи (следует ли возвращать успех или объект). Если нет существующего соглашения, я согласен с lucasbfr и предлагаю вам вернуть успех (что согласуется с тем, как разработан TryParse (...)).
В некоторых случаях использование исключения - плохая идея (особенно при написании сервера). Вам понадобятся два варианта метода. Также посмотрите на класс словаря, чтобы узнать, что вам следует делать.
// NB: A bool is the return value.
// This makes it possible to put this beast in if statements.
public bool TryCalculatePoint(... out Point result) { }
public Point CalculatePoint(...)
{
Point result;
if (!TryCalculatePoint(... out result))
throw new BogusPointException();
return result;
}
Лучшее обоих миров!
Я согласен, что идея try в некоторых случаях хороша, но ваше утверждение о том, что генерирование исключения на сервере является настолько неправильным, что я не уверен, с чего начать.
Я не говорил никогда не бросать исключения :). Только тогда, когда вам это абсолютно необходимо. Как бы вы справились с ошибками синтаксического анализа XML на сервере XMPP с, скажем, 10000 активных пользователей? Написание TCP-сервера для огромной базы пользователей сильно отличается от веб-приложений, в которых соединения удерживаются только в течение нескольких миллисекунд.
@Jonathon: Сказать, что для сервера вы не должны генерировать исключение, просто неправильно. Это указывает мне, что вы используете исключения для управления потоком, что еще более неправильно.
@ rich-b не использует его для управления потоком. На моем сервере есть дерево patricia, используемое для каждой строфы, которую получает мой сервер (5000 p / m). Если узел не может быть найден, он отправляется на другой сервер xmpp через S2S. Следует ли мне делать исключения здесь? Или вернуть false?
Bool TrySomething () - это, по крайней мере, практика, которая работает нормально для методов синтаксического анализа .net, но я не думаю, что мне это нравится в целом.
Создание исключения часто является хорошим делом, хотя его не следует использовать в ситуациях, которые могут возникнуть во многих обычных ситуациях, и это связано с затратами на производительность.
Возвращать null, когда это возможно, в большинстве случаев нормально, когда вам не нужно исключение.
тем не мение - ваш подход немного процедурный - как насчет создания чего-то вроде класса PointCalculator - принятия необходимых данных в качестве параметров в конструкторе? Затем вы вызываете на нем CalculatePoint и получаете доступ к результату через свойства (отдельные свойства для Point и для Success).
Это действительно хорошая идея? Я не хочу создавать класс для каждой пары методов, в итоге я бы получил сотни.
@ Tobj0rn: Я склонен согласиться, я думаю, что это симптом более серьезной проблемы в его кодовой базе.
Я использовал ту же модель, которую MS использует с методами TryParse различных классов.
Ваш исходный код:
public Point CalculatePoint (... out Boolean boSuccess);
общедоступный список CalculateListOfPoints (... out Boolean boSuccess);
Превратится в
public bool CalculatePoint (... out (или ref) Point CalculatedValue);
public bool CalculateListOfPoints (... out (или ref) List CalculatedValues);
В основном вы делаете успех / неудачу возвращаемым значением.
Вы не хотите генерировать исключения, когда что-то ожидается, поскольку @Kevin заявил, что исключения предназначены для исключительных случаев.
Вы должны вернуть то, что ожидается в случае «неудачи», как правило, я выбрал нулевое значение в качестве плохого возврата.
Документация для вашего метода должна информировать пользователей о том, чего ожидать, когда данные не считается.
Подводя итог, есть несколько подходов, которые вы можете предпринять:
В зависимости от сценария я обычно использую комбинацию возврата null или выдачи исключения, поскольку они кажутся мне «самыми чистыми» и лучше всего подходят для существующей кодовой базы в компании, в которой я работаю. Итак, моя личная передовая практика - подходы 1 и 2.
В основном это зависит от поведения ваших методов и их использования.
Если сбой является обычным и некритическим, тогда пусть ваши методы возвращают логическое значение, указывающее на их успех, и используют параметр out для передачи результата. Поиск ключа в хэше, попытка чтения данных из неблокирующего сокета, когда данные недоступны, все эти примеры попадают в эту категорию.
Если сбой является неожиданным, верните непосредственно результат и передайте ошибки с исключениями. Открытие файла только для чтения, подключение к TCP-серверу - хорошие кандидаты.
И иногда оба варианта имеют смысл ...
Почему они потерпели неудачу? Если это из-за чего-то, что сделал вызывающий (например, предоставленных аргументов), тогда бросок ArgumentException вполне уместен. Можно использовать метод Try [...], который позволяет избежать исключения.
Я думаю, что было бы неплохо предоставить версию, которая генерирует исключение, чтобы вызывающие абоненты, которые ожидают, что они всегда будут предоставлять хорошие данные, получат достаточно сильное сообщение (т.е. исключение), если они когда-нибудь ошибаются.
Если сбой произошел по определенной причине, я думаю, что можно вернуть null или bool и иметь параметр out. Однако, если вы вернете null независимо от сбоя, я не рекомендую это делать. Исключения предоставляют обширный набор информации, включая причину, ПОЧЕМУ что-то не удалось, если все, что вы вернули, является нулевым, то как узнать, что это из-за неправильных данных, у вас закончилась память или какое-то другое странное поведение.
Даже в .net у TryParse есть брат Parse, так что вы можете получить исключение, если хотите.
Если бы я предоставил метод TrySomething, я бы также предоставил метод Something, который выдает исключение в случае сбоя. Тогда дело за вызывающим.
Однажды мы написали целый фреймворк, в котором все общедоступные методы либо возвращали true (выполнялись успешно), либо false (произошла ошибка). Если нам нужно было вернуть значение, мы использовали выходные параметры. Вопреки распространенному мнению, такой способ программирования действительно упростил наш код.
О Боже. Если вы сделали это на языке с обработкой исключений, опубликуйте код. У меня будет новая история на первой полосе TDWTF.
Что ж, с Point вы можете отправить Point.Empty в качестве возвращаемого значения в случае сбоя. Теперь все это действительно возвращает точку с 0 для значений X и Y, поэтому, если это может быть допустимое возвращаемое значение, я бы держался подальше от этого, но если ваш метод никогда не вернет точку (0,0) , тогда вы можете использовать это.
Вернуть Point.Empty. Это шаблон дизайна .NET, чтобы возвращать специальное поле, когда вы хотите проверить, было ли создание структуры успешным. По возможности избегайте параметров из.
public static readonly Point Empty
Зачем избегать параметров out? На самом деле я стараюсь избегать их, но это потому, что я нахожу их менее ясными в намерениях.
Параметры Out (и другие виды указателей) могут быть непростыми для новичков. Это типичный источник семантических ошибок. Microsoft также предлагает этого избежать: msdn.microsoft.com/en-us/library/ms182131(VS.80).aspx
Шаблон, с которым я экспериментирую, возвращает Может быть. Он имеет семантику шаблона TryParse, но схожую сигнатуру с шаблоном возврата с нулевым значением при ошибке.
Я еще не уверен в том или ином, но предлагаю это на ваше коллективное рассмотрение. Преимущество этого метода заключается в том, что перед вызовом метода не требуется определять переменную для хранения выходного параметра в месте вызова метода. Он также может быть расширен коллекцией Ошибки или Сообщения, чтобы указать причину сбоя.
Класс Maybe выглядит примерно так:
/// <summary>
/// Represents the return value from an operation that might fail
/// </summary>
/// <typeparam name = "T"></typeparam>
public struct Maybe<T>
{
T _value;
bool _hasValue;
public Maybe(T value)
{
_value = value;
_hasValue = true;
}
public Maybe()
{
_hasValue = false;
_value = default(T);
}
public bool Success
{
get { return _hasValue; }
}
public T Value
{
get
{ // could throw an exception if _hasValue is false
return _value;
}
}
}
Извините, я только что вспомнил о типе Nullable, вам стоит взглянуть на него. Я не совсем уверен, что это за накладные расходы.
Просто точка разработки ... Если вы возвращаете коллекцию, если вы не добились успеха, не возвращайте нуль, просто верните пустую коллекцию. Я голосую за TryCalculate, возвращающую логическое значение.