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

С#: перетасовка пользовательских исключений

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

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

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

Исключение:

class FooException : Exception
{
    //...
}

Вариант 1: Foo замаскирует все Исключения:

class Foo
{
    DoSomething(int param)
    {
        try 
        {
             if (/*Something Bad*/)
             {  
                 //violates business logic etc... 
                 throw new FooException("Reason...");
             }
             //... 
             //something that might throw an exception
        }
        catch (FooException ex)
        {
             throw;
        }
        catch (Exception ex)
        {
             throw new FooException("Inner Exception", ex);
        }
    }
}

Вариант 2: Foo выбрасывает определенные FooExceptions, но позволяет пропустить другие Исключения:

class Foo
{
    DoSomething(int param)
    {
        if  (/*Something Bad*/)
        {
             //violates business logic etc... 
             throw new FooException("Reason...");
        }
        //... 
        //something that might throw an exception and not caught
    }
}
4b9b3361

Ответ 1

Основываясь на моем опыте работы с библиотеками, вы должны обернуть все (что вы можете ожидать) в FooException по нескольким причинам:

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

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

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

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

Ответ 2

Посмотрите на лучшие методы MSDN.

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

Ответ 3

Я всегда добавляю несколько свойств при создании настраиваемого исключения. Одним из них является имя пользователя или идентификатор. Я добавляю свойство DisplayMessage для переноса текста, который будет отображаться для пользователя. Затем я использую свойство Message для передачи технических данных в журнал.

Я улавливаю каждую ошибку в Уровне доступа к данным на уровне, где я все еще могу захватить имя хранимой процедуры и значения переданных параметров. Или встроенный SQL. Возможно, имя базы данных или частичное соединение (без учетных данных, пожалуйста). Они могут отображаться в сообщении или в собственном новом пользовательском свойстве DatabaseInfo.

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

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

Ответ 4

Цель пользовательских исключений - предоставить подробную, контекстуальную информацию для stacktrace, чтобы помочь в отладке. Вариант 1 лучше, потому что без него вы не получите "происхождение" исключения, если оно произошло "ниже" в стеке.

Ответ 5

если вы запустите фрагмент кода для "Исключения" в Visual Studio, у вас есть шаблон написания исключения из практики.

Ответ 6

Примечание Вариант 1: ваш throw new FooException("Reason..."); не будет пойман, поскольку он находится вне блока try/catch

  • Вы должны только перехватывать исключения, которые хотите обработать.
  • Если вы не добавляете никаких дополнительных данных в исключение, чем используйте throw;, так как он не будет убивать ваш стек. В Варианте 2 вы все равно можете выполнить некоторую обработку внутри catch и просто вызвать throw;, чтобы восстановить исходное исключение с исходным стеком.

Ответ 7

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

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

    Очевидно, что между этими крайностями часто бывают другие возможные состояния. Я бы предположил, что нужно стремиться к созданию специального исключения, которое явно указывает, что состояние №1 существует и, возможно, одно для # 2, если это может привести к непредвиденным, но неизбежным обстоятельствам. Любые исключения, которые происходят и приводят к состоянию # 1, должны быть обернуты в объект исключения, указывающий состояние # 1. Если исключения могут происходить таким образом, что состояние системы может быть скомпрометировано, их следует либо обернуть как # 2, либо разрешить перколяцию.

Ответ 8

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

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