Вопрос

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

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

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

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

Другой путь — вернуть логическое значение boSuccess, получить результат (Point или List) в качестве выходного параметра и вызвать их TryToCalculatePoint или что-то в этом роде...

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

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

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

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

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

Ваш исходный код:
общественная точка CalculatePoint(...out Boolean boSuccess);
публичный список CalculateListOfPoints(...out Boolean boSuccess);

Превратится в общедоступную Bool Расчеты (...out (или ref) Point CalculatedValue);
public bool CalculateListOfPoints(...out (или ref) List CalculatedValues);

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

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

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

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

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

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

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

А иногда оба пути имеют смысл...

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

public static readonly Point Empty

Шаблон, с которым я экспериментирую, возвращает Может быть.Имеет семантику 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; 
            }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top