Подтвердить что ты не робот

Должен ли я выбросить собственное ArgumentOutOfRangeException или позволить одному пузырю снизу?

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Если вы используете .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. Они просто забыли это или код, который я написал выше, против предполагаемого использования?

Ответ 3

... breaks m [y] Цель OOP для пользователя, которому не нужно знать об основных объектах.

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

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

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

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

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

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

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

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

Ответ 4

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

изменить

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

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

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

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

изменить

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