Обработка исключений в настраиваемом классе

У меня есть класс с частным полем Dictionary<string, T> (users), где ключ string представляет имя пользователя. Я написал несколько методов для этого класса, например, метод GetValue(string userid) возвращает объект T, связанный с указанным именем пользователя.

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

  • если указанный ключ равен нулю, Dictionary выдает ArgumentNullException;
  • если указанный ключ не существует в Dictionary, он выдает KeyNotFoundException.

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

public T GetValue(string userid)
{
    try
    {
        return users[userid];
    }
    catch(ArgumentNullException ex1)
    {
        // re-throw the same exception
    }
    catch(KeyNotFoundException ex2)
    {
        throw new UserIdNotFoundException("The specified userid does not exist.", ex2);   // custom exception
    }
}

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

public T GetValue(string userid)
{
    if (userid != null)
    {
        if(users.ContainsKey(userid))
            return users[userid];
        else throw new UserIdNotFoundException("The specified userid does not exist.", ex2);   // custom exception
    }
    else throw new ArgumentNullException(...);
}

Третий подход аналогичен второму, но в нем используется метод TryGetValue из Dictionary.

public T GetValue(string userid)
{
    if (userid != null)
    {
        T value;
        if(users.TryGetValue(userid, out value))
            return value;
        else throw new UserIdNotFoundException("The specified userid does not exist.", ex2);   // custom exception
    }
    else throw new ArgumentNullException(...);
}

Каковы преимущества и недостатки каждого из этих подходов? В передовых методах обработки исключений говорится следующее:

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

В моем случае метод GetValue(string userid) будет вызываться редко, но потенциальные пользователи класса могут использовать его очень часто.


person enzom83    schedule 23.02.2012    source источник


Ответы (4)


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

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

person supercat    schedule 23.02.2012

Это зависит от целевого назначения вашего класса. Если ваш класс должен действовать как оболочка словаря, например, для клиентов может быть полезно распространить подобное поведение ошибки / исключения типичного Dictionary. Если ваш класс реализует что-то более сложное (скорее всего), я бы сказал, что вам лучше представить свои собственные ошибки вызывающему. Что касается того, какой подход выбрать, выполните ту же проверку ввода, что и при обычном Dictionary взаимодействии. Другими словами, если вы обычно проверяете ContainsKey или TryGetValue перед добавлением в Dictionary, продолжайте делать это здесь, но тогда нет необходимости создавать новое исключение. Вместо этого сообщите об ошибке вызывающему абоненту на понятном ему языке. Им не нужно знать, что вы используете Dictionary под капотом.

person Adam S    schedule 23.02.2012

ContainsKey и TryGetValue оба метода могут избежать выброса KeyNotFoundException

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

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

Подробнее: Объект словаря (ContainsKey против TryGetValue) может вам помочь.

person Pranay Rana    schedule 23.02.2012

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

Вторая реализация неоптимальна: она обращается к словарю дважды: один раз для проверки существования и второй раз для получения значения.

Первая реализация - наихудшая:

  • Это не является функционально эквивалентным, если аргумент равен нулю.
  • Он получает снижение производительности из-за оператора try - компилятору, очевидно, придется установить некоторые данные в стеке.
  • На мой взгляд, существует также проблема стиля, связанная с программами, широко использующими операторы catch. Лучшее, что можно сделать с исключениями, - это позволить им всплывать до тех пор, пока они не будут зарегистрированы или что-то с ними не будет сделано.
person ZunTzu    schedule 23.02.2012