Должен ли я выбросить свое собственное ArgumentOutOfRangeException или позволить одному пузырю снизу вверх? - PullRequest
12 голосов
/ 26 марта 2010

У меня есть класс, который оборачивает List <>

У меня есть GetValue по методу индекса:

    public RenderedImageInfo GetValue(int index)
    {
        list[index].LastRetrieved = DateTime.Now;
        return list[index];
    }

Если пользователь запрашивает индекс, выходящий за пределы диапазона, это вызывает исключение ArgumentOutOfRangeException.

Должен ли я просто позволить этому случиться или проверить это и бросить свое? т.е. * +1008 *

    public RenderedImageInfo GetValue(int index)
    {
        if (index >= list.Count)
        {
            throw new ArgumentOutOfRangeException("index");
        }
        list[index].LastRetrieved = DateTime.Now;
        return list[index];
    }

В первом сценарии у пользователя будет исключение из внутреннего списка, что нарушает мою цель ООП - пользователю не нужно знать о базовых объектах.

Но во втором сценарии мне кажется, что я добавляю избыточный код.

Edit:
И теперь, когда я думаю об этом, как насчет третьего сценария, где я ловлю внутреннее исключение, изменяю его и перебрасываю его?

Ответы [ 4 ]

12 голосов
/ 26 марта 2010

Вы должны бросить свой собственный, по нескольким причинам:

  1. Вы можете явно установить соответствующее имя параметра в конструкторе . Таким образом, исключение имеет соответствующую информацию о параметре для аргумента, выходящую за пределы диапазона.
  2. (Незначительно) Исключение из внутреннего списка будет иметь недопустимую трассировку стека, если это касается вашего пользователя. Создав новое исключение, вы можете получить трассировку стека, показывающую, что ваш метод является неподходящим, что облегчит отладку вашего пользователя.

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

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

3 голосов
/ 26 марта 2010

... ломает m [y] ООП цель пользователя, которому не нужно знать о базовых объектах.

Бросок того же исключения ничего не делает для инкапсуляции. В вашем договоре (подразумеваемом / задокументированном - поскольку у нас нет проверенных исключений или DbC) указано, что в этом случае вы выбросите исключение ArgumentOutOfRange или нет. Как генерируется это исключение и как выглядит его трассировка стека, не имеет значения для вызывающей стороны.

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

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

Кстати, совет (о котором вы, возможно, думаете) об обертывании исключений исходит из более абстрактного сценария. Рассмотрим IAuthenticationService, который выбрасывает, если пользователь не может Login,

Если есть реализации LdapAuthenticationService и DatabaseAuthenticationService, то вам нужно будет отловить и LdapDirectoryException, и SqlException, чтобы определить неудачный вход в систему. Когда 3-я реализация будет завершена, вам нужно будет добавить также ее конкретные типы исключений. Всем реализаторам, заключившим свое конкретное исключение в FailedAuthenticationException, вам нужно беспокоиться только об одном типе.

Хотя было бы неплохо включить исходное исключение в InnerException, поскольку оно помогает в отладке.

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

Все это, как говорится, если бы это было для библиотеки - тогда я бы проверил это и бросил. Но не для чистоты ООП, а потому, что это делает контракт явным и с меньшей вероятностью непреднамеренного изменения. Если бы я использовал [T | B] DD, то я просто написал бы для него тест и позволил бы вместо него List throw - тест делает контракт явным.

3 голосов
/ 26 марта 2010

Если вы используете .NET 2.0 или выше, вы можете использовать внутреннее исключение , например:

public RenderedImageInfo GetValue(int index)
{
    try {
        list[index].LastRetrieved = DateTime.Now;
        return list[index];
    } catch (ArgumentOutOfRangeException ex) {
        throw new ArgumentOutOfRangeException("index", ex);
    }
}

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

Редактировать : теперь я вижу, что InnerException поддерживается с .NET 1.0, это просто новый конструктор. Я не вижу способа на самом деле установить InnerException для ArgumentOutOfRangeException в .NET до 2.0. Они просто забыли это, или код, который я написал выше, противоречит предполагаемому использованию?

2 голосов
/ 26 марта 2010

Хорошо, если в этом случае всплывет исключение.

редактировать

Меня попросили обновить мой ответ, чтобы включить некоторые из ответов ...

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

Итак, оно того стоит? Это зависит. Это сводится к анализу затрат / выгод, который может быть сделан только для конкретного контекста. Для общедоступного API, такого как AlphaFS, преимущества могут значительно превышать затраты. Для внутреннего использования в коде, который не предназначен для значительного повторного использования, это, вероятно, не так. Это контекст моего первоначального ответа, который я до сих пор поддерживаю. Кроме того, как указывал Hightechrider, могут существовать и другие технологии, такие как контракты на кодирование, которые дают практически то же преимущество, но с меньшими затратами, изменяя уравнение в пользу надежности.

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

редактировать

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...