Как указать, что метод не удался

У меня есть несколько подобных методов, например, например. 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 или что-то в этом роде ...

Какая лучшая практика?

Изменить: я не хочу использовать исключения для управления потоком! Иногда ожидается неудача.


person Joel in Gö    schedule 02.10.2008    source источник
comment
Просто точка разработки ... Если вы возвращаете коллекцию, если вы не добились успеха, не возвращайте нуль, просто верните пустую коллекцию. Я голосую за TryCalculate, возвращающую логическое значение.   -  person    schedule 02.10.2008
comment
Метод, не завершающийся из-за параметра вне диапазона, не является «управлением потоком», а является неожиданным и исключительным условием.   -  person GEOCHET    schedule 02.10.2008
comment
@Will: Я согласен, это то, что я имел в виду под пустым списком. @Rich: Согласен; но параметры могут быть в порядке, просто может не быть подходящей точки для возврата.   -  person Joel in Gö    schedule 02.10.2008
comment
@Joel: Тогда вам может понадобиться шаблон try, однако для того, что вы указали в вопросе, вы должны создать исключение.   -  person GEOCHET    schedule 02.10.2008


Ответы (16)


Лично я бы использовал ту же идею, что и TryParse (): используя параметр out для вывода реального значения и возвращая логическое значение, указывающее, был ли вызов успешным или нет.

public bool CalculatePoint(... out Point result);

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

person Luk    schedule 02.10.2008
comment
@lucasbfr: +1, если вы предоставляете DoX, который может вызвать исключение, которое вы можете определить, предоставьте TryX. - person user7116; 02.10.2008
comment
@sixlettervariables: я согласен, я не хотел менять имя метода, потому что он не спрашивал, но это было бы очень хорошей практикой - person Luk; 02.10.2008
comment
Хотя концепция TryParse иногда бывает утомительной, потому что вам всегда нужно объявлять переменную, я привык к ней и всегда предпочитаю ее версии с генерацией исключений. Однако всякий раз, когда вы можете просто вернуть null, я делаю это вместо подхода TryParse. - person OregonGhost; 02.10.2008
comment
Если вы собираетесь это сделать, я бы изменил имя метода на «Попробовать вычислить точку». - person Omar Kooheji; 02.10.2008

Почему они проиграли? Если это из-за чего-то, что сделал вызывающий (то есть предоставленных аргументов), тогда бросок ArgumentException вполне уместен. Можно использовать метод Try [...], который позволяет избежать исключения.

Я думаю, что было бы неплохо предоставить версию, которая генерирует исключение, чтобы вызывающие абоненты, которые ожидают, что они всегда будут предоставлять хорошие данные, получат достаточно сильное сообщение (т. Е. Исключение), если они когда-нибудь ошибаются.

person Jon Skeet    schedule 02.10.2008

Другой альтернативой является создание исключения. Однако обычно вы хотите создавать исключения только в «исключительных случаях».

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

person Kevin    schedule 02.10.2008

Если сбой произошел по определенной причине, я думаю, что можно вернуть null или bool и иметь параметр out. Если, однако, вы вернете null независимо от сбоя, я не рекомендую это делать. Исключения предоставляют обширный набор информации, включая причину, ПОЧЕМУ что-то не удалось; если все, что вы вернули, является нулевым, то как узнать, что это из-за неправильных данных, у вас закончилась память или какое-то другое странное поведение.

Даже в .net у TryParse есть брат Parse, так что вы можете получить исключение, если хотите.

Если бы я предоставил метод TrySomething, я бы также предоставил метод Something, который выдает исключение в случае сбоя. Тогда дело за вызывающим.

person Kepboy    schedule 02.10.2008

Я использовал ту же модель, которую MS использует с методами TryParse различных классов.

Ваш исходный код:
public Point CalculatePoint (... out Boolean boSuccess);
public List CalculateListOfPoints (... out Boolean boSuccess);

Превратится в public bool CalculatePoint (... out (или ref) Point CalculatedValue);
public bool CalculateListOfPoints (... out (или ref) List CalculatedValues);

В основном вы делаете успех / неудачу возвращаемым значением.

person Brad Bruce    schedule 02.10.2008

Подводя итог, есть несколько подходов, которые вы можете предпринять:

  1. Если возвращаемый тип является типом значения, например Point, использовать функцию C #, допускающую значение NULL, и вернуть Point? (также известный как Nullable), таким образом вы все равно можете вернуть null при сбое
  2. Выбрасывать исключение при сбое. Вся аргументация / обсуждение того, что является «исключительным», а что нет, - это спорный вопрос, это ваш API, вы решаете, что является исключительным поведением.
  3. Примите модель, аналогичную той, которая реализована Microsoft в базовых типах, таких как Int32, предоставьте CalculatePoint и TryCalculatePoint (int32.Parse и int32.TryParse) и сделайте один бросок и один возврат логического значения.
  4. Верните универсальную структуру из ваших методов, которая имеет два свойства: bool Success и GenericType Value.

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

person Rob    schedule 02.10.2008

В основном это зависит от поведения ваших методов и их использования.

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

Если сбой является неожиданным, верните непосредственно результат и передайте ошибки с исключениями. Открытие файла только для чтения, подключение к TCP-серверу - хорошие кандидаты.

И иногда оба варианта имеют смысл ...

person bltxd    schedule 02.10.2008

Верните Point.Empty. Это шаблон дизайна .NET, чтобы возвращать специальное поле, когда вы хотите проверить, было ли создание структуры успешным. По возможности избегайте out параметров.

public static readonly Point Empty
person artur02    schedule 02.10.2008
comment
Зачем избегать параметров out? На самом деле я стараюсь избегать их, но это потому, что я нахожу их менее ясными в намерениях. - person Joel in Gö; 03.10.2008
comment
Параметры Out (и другие виды указателей) могут быть непростыми для новичков. Это типичный источник семантических ошибок. Microsoft также предлагает избегать этого: msdn.microsoft.com/ en-us / library / ms182131 (VS.80) .aspx - person artur02; 04.10.2008

Шаблон, с которым я экспериментирую, возвращает Может быть. Он имеет семантику шаблона TryParse, но схожую сигнатуру с шаблоном возврата с нулевым значением при ошибке.

Я еще не уверен в том или ином, но предлагаю это на ваше коллективное рассмотрение. Его преимущество заключается в том, что перед вызовом метода не требуется, чтобы переменная определялась для хранения выходного параметра в месте вызова метода. Его также можно дополнить коллекцией Errors или Messages, чтобы указать причину сбоя.

Класс 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; 
            }
    }
}
person Samuel Jack    schedule 02.10.2008

Я бы сказал, что лучшая практика - возвращаемое значение означает успех, а исключение / a> означает неудачу.

В приведенных вами примерах я не вижу причин, по которым вам не следует использовать " rel="nofollow noreferrer" исключения в случае сбоя.

person GEOCHET    schedule 02.10.2008
comment
Исключения не следует использовать в неисключительных случаях. Здесь нужно быть осторожным. - person Kevin; 02.10.2008
comment
Я не сторонник использования исключений для нормального поведения (если вы ожидаете, что функция не будет работать для некоторых записей). - person Luk; 02.10.2008
comment
@Kevin: исключительный случай, например, невозможность завершить метод? - person GEOCHET; 02.10.2008
comment
@lucasbfr: Пожалуйста, объясните, где вы считаете отказ этого метода «нормальным» поведением. Я вообще не вижу этой болтовни в вопросе. - person GEOCHET; 02.10.2008
comment
А как насчет игр, серверов и графических приложений? Его пример характерен как минимум для игры или графического приложения. Исключения бывают медленными: очень и очень медленными. Всегда давайте потенциальным разработчикам возможность избежать исключений. - person Jonathan C Dickinson; 02.10.2008
comment
Rich B - если такой сбой ожидается, то я бы не стал считать это исключительным случаем. - person Aaron Powell; 02.10.2008
comment
@Kevin: TryParse - это совершенно другая тема, чем исключения, и это сделано специально. Если у вас есть метод, и он принимает ввод, и этот ввод приводит к состоянию, когда метод не может возвращать ожидаемым образом, должно быть сгенерировано исключение. Конец истории. - person GEOCHET; 02.10.2008
comment
@Jonathon: Это смешно. Исключение не будет использоваться для управления потоком. Нет никаких причин, по которым он не должен быть обязан проверять свой ввод в первую очередь. Но чтобы не использовать исключения из-за того, что они находятся в игре или на сервере? Это смешно. - person GEOCHET; 02.10.2008
comment
См. Редактирование: это, конечно, не подходящая ситуация для исключения. - person Joel in Gö; 02.10.2008
comment
И сбой не имеет ничего общего с границами или грязным вводом; это действительно графическое приложение, и метод не работает, если нет точки, которая удовлетворяет набору сложных условий - person Joel in Gö; 02.10.2008
comment
@Joel: Тогда попытка может быть вашим лучшим решением, однако я думаю (как ответил другой человек) вы, возможно, захотите реорганизовать эту часть вашего кода. Я думаю, вы ошибаетесь. - person GEOCHET; 02.10.2008
comment
@Rich: Почему неправильный подход? (это настоящий вопрос!) Как вы посоветуете провести рефакторинг? - person Joel in Gö; 02.10.2008
comment
@ Джоэл и Кевин: Я знаю, где использовать исключения. Если бы ваш код был правильно структурирован, это не было бы проблемой. Каждый раз, когда вы находитесь в ситуации, когда вы думаете, что возврат кода ошибки может быть полезным, с вашим кодом что-то не так. - person GEOCHET; 02.10.2008

В некоторых случаях использование исключения - плохая идея (особенно при написании сервера). Вам понадобятся два варианта метода. Также посмотрите на класс словаря, чтобы узнать, что вам следует делать.

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

Лучшее из обоих миров!

person Jonathan C Dickinson    schedule 02.10.2008
comment
Я согласен, что в некоторых случаях идея попытки хороша, но ваше утверждение о том, что генерировать исключение на сервере, настолько неверно, что я не уверен, с чего начать. - person GEOCHET; 02.10.2008
comment
Я не говорил никогда не бросать исключения :). Только тогда, когда вам это абсолютно необходимо. Как бы вы справились с ошибками синтаксического анализа XML на сервере XMPP с, скажем, 10000 активных пользователей? Написание TCP-сервера для огромной базы пользователей сильно отличается от веб-приложений, в которых соединения удерживаются только в течение нескольких миллисекунд. - person Jonathan C Dickinson; 02.10.2008
comment
@Jonathon: Сказать, что для сервера вы не должны генерировать исключение, просто неправильно. Это указывает мне на то, что вы используете исключения для управления потоком, что еще более неправильно. - person GEOCHET; 02.10.2008
comment
@ rich-b не использует его для управления потоком. На моем сервере есть дерево patricia, используемое для каждой строфы, которую получает мой сервер (5000 p / m). Если узел не может быть найден, он отправляется на другой сервер xmpp через S2S. Следует ли мне делать исключения здесь? Или вернуть false? - person Jonathan C Dickinson; 08.10.2008

Bool TrySomething () - это, по крайней мере, практика, которая работает нормально для методов синтаксического анализа .net, но я не думаю, что мне это нравится в целом.

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

Возвращать null, когда это возможно, в большинстве случаев нормально, когда вам не нужно исключение.

Однако - ваш подход немного процедурный - как насчет создания чего-то вроде класса PointCalculator - принятия необходимых данных в качестве параметров в конструкторе? Затем вы вызываете на нем CalculatePoint и получаете доступ к результату через свойства (отдельные свойства для Point и для Success).

person Torbjørn    schedule 02.10.2008
comment
Это действительно хорошая идея? Я не хочу создавать класс для каждой пары методов, в итоге я бы получил сотни. - person Joel in Gö; 02.10.2008
comment
@ Tobj0rn: Я склонен согласиться, я думаю, что это симптом более серьезной проблемы в его кодовой базе. - person GEOCHET; 02.10.2008

Вы не хотите генерировать исключения, когда что-то ожидается, поскольку @Kevin заявил, что исключения предназначены для исключительных случаев.

Вы должны вернуть то, что ожидается в случае «неудачи», как правило, я выбрал нулевой вариант плохого возврата.

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

person Aaron Powell    schedule 02.10.2008

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

person ilitirit    schedule 02.10.2008
comment
О Боже. Если вы сделали это на языке с обработкой исключений, опубликуйте код. У меня будет новая история на первой полосе TDWTF. - person GEOCHET; 02.10.2008

Что ж, с Point вы можете отправить Point.Empty в качестве возвращаемого значения в случае сбоя. Теперь все, что это действительно делает, это возвращает точку с 0 для значений X и Y, поэтому, если это может быть допустимое возвращаемое значение, я бы держался подальше от этого, но если ваш метод никогда не вернет точку (0,0) , тогда вы можете использовать это.

person BFree    schedule 02.10.2008

Извините, я только что вспомнил о типе Nullable, вам стоит взглянуть на него. Я не совсем уверен, что это за накладные расходы.

person Jonathan C Dickinson    schedule 08.10.2008