Как указать, что метод был неудачным

#c# #methods #return-value

Вопрос:

У меня есть несколько подобных методов, скажем, например. CalculatePoint(…) и CalculateListOfPoints(…). Иногда они могут не удаться, и необходимо указать на это вызывающему абоненту. Для CalculateListOfPoints, который возвращает общий список, я мог бы вернуть пустой список и потребовать, чтобы вызывающий объект проверил это; однако Точка является типом значения, и поэтому я не могу вернуть там значение null.

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

 public Point CalculatePoint(... out Boolean boSuccess);
public List<Point> CalculateListOfPoints(... out Boolean boSuccess);
 

или альтернативно вернуть очко? для точки вычисления и возвращает значение null, указывающее на сбой. Это означало бы необходимость вернуться к типу, не допускающему обнуления, что кажется чрезмерным.

Другим путем было бы вернуть логический процесс, указать результат (точку или список) в качестве параметра «out» и назвать их TryToCalculatePoint или что-то в этом роде…

Что такое наилучшая практика?

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

Комментарии:

1. Просто дизайнерский момент… Если вы возвращаете коллекцию, если вы не добьетесь успеха, не возвращайте значение null, просто верните пустую коллекцию. Я голосую за то, чтобы TryCalculate возвращал логическое значение.

2. Метод, не завершенный из-за параметра вне диапазона, не является «управлением потоком», это неожиданное и исключительное условие.

3. @Уилл: Я согласен, это то, что я имел в виду под пустым списком. @Rich: Согласен; но параметры могут быть в порядке, просто может не быть подходящей точки для возврата.

4. @Joel: Тогда вам может понадобиться шаблон try, однако для того, что вы указали в вопросе, вы должны создать исключение.

Ответ №1:

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

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

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

Комментарии:

1. @lucasbfr: 1, если вы предоставите DoX, который может вызвать исключение, которое вы можете выяснить, предоставьте TryX.

2. @sixlettervariables: Я согласен, я не хотел менять название метода, потому что он не спрашивал, но это было бы очень хорошей практикой

3. В то время как концепция пробного анализа иногда становится утомительной, потому что вам всегда нужно объявлять переменную, я привык к ней и всегда предпочитал ее версии с отбрасыванием исключений. Однако всякий раз, когда вы можете просто вернуть null, я делаю это вместо подхода TryParse.

4. Если вы собираетесь это сделать, я бы изменил имя метода, чтобы попытаться вычислить точку.

Ответ №2:

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

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

Ответ №3:

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

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

Ответ №4:

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

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

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

Ответ №5:

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

Ваш исходный код:
public Point CalculatePoint(… из логического процесса);
public List CalculateListOfPoints(… из логического процесса);

Превратится в общедоступную вычисляемую точку bool(… Значение вычисленной точки out (или ref));
общедоступные вычисляемые точки bool(… Список вычисляемых значений out (или ref));

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

Ответ №6:

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

  1. Когда возвращаемый тип является типом значения, например Точкой, используйте функцию обнуления C# и возвращайте точку? (он же обнуляемый), таким образом, вы все равно можете вернуть значение null при сбое
  2. Создавайте исключение при возникновении сбоя. Весь спор/дискуссия о том, что является и не является «исключительным», является спорным вопросом, это ваш API, вы решаете, какое поведение является исключительным.
  3. Используйте модель, аналогичную той, которая реализована Microsoft в базовых типах, таких как Int32, предоставьте расчетную точку и попробуйте вычислить точку (int32.Parse и int32.Попробуйте проанализировать) и сделайте один бросок и один возврат bool.
  4. Верните универсальную структуру из ваших методов, которая имеет два свойства: Успех bool и значение GenericType.

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

Ответ №7:

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

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

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

И иногда оба способа имеют смысл…

Ответ №8:

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

 public static readonly Point Empty
 

Комментарии:

1. Почему следует избегать параметров out? На самом деле я стараюсь избегать их, но это потому, что я нахожу их менее ясными в намерениях.

2. Параметры вывода (и другие указатели) могут быть сложными для начинающих. Это типичный источник семантических ошибок. Microsoft также предлагает избегать этого: msdn.microsoft.com/en-us/library/ms182131(ПРОТИВ 80).aspx

Ответ №9:

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

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

Класс «Может быть» выглядит примерно так:

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

Ответ №10:

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

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

Комментарии:

1. Исключения не должны использоваться в не исключительных случаях. Здесь тебе нужно быть осторожным.

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

3. @Kevin: Исключительный случай, например, невозможность завершить метод?

4. @lucasbfr: Пожалуйста, объясните, в чем вы видите неудачу этого метода как «нормальное» поведение. Я вообще не вижу этого словосочетания в вопросе.

5. А как насчет игр, серверов и графических приложений? Его пример указывает, по крайней мере, на игру или графическое приложение. Исключения бывают медленными: очень, очень медленными. Всегда давайте потенциальным разработчикам возможность избежать исключений.

Ответ №11:

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

 // 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 resu<
   if(!TryCalculatePoint(... out result))
       throw new BogusPointException();
   return resu<
}
 

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

Комментарии:

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

2. Я не говорил, что никогда не выбрасывайте исключения :). Только когда тебе это абсолютно необходимо. Как бы вы обрабатывали ошибки синтаксического анализа XML на сервере XMPP с, скажем, 10000 активными пользователями? Написание TCP-сервера для огромного количества пользователей сильно отличается от веб-приложений, где соединения поддерживаются всего за несколько миллионов.

3. @Jonathon: Говорить, что для сервера вы не должны создавать исключения, просто неправильно. Это указывает мне на то, что вы используете исключения для управления потоком, что еще более неправильно.

4. @rich-b не использует его для управления потоком. На моем сервере есть патриция три, используемая для каждой строфы, которую получает мой сервер (5000 р/м). Если узел не может быть найден, он отправляется на другой сервер xmpp через S2S. Должен ли я делать исключения здесь? Или вернуть ложь?

Ответ №12:

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

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

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

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

Комментарии:

1. Это действительно хорошая идея? Я не хочу создавать класс для каждой пары методов, у меня будут сотни.

2. @Tobj0rn: Я склонен согласиться, я думаю, что это симптом более серьезной проблемы в его кодовой базе.

Ответ №13:

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

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

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

Ответ №14:

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

Комментарии:

1. О боже. Если вы сделали это на языке с обработкой исключений, пожалуйста, опубликуйте код. У меня будет новая история на первой полосе для TDWTF.

Ответ №15:

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

Ответ №16:

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