Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Пробрасывание исключений

#c_sharp


Скажите, пожалуйста, пробрасывание исключений на верх это нормальная практика?  

Просто сейчас в проекте своем учебном попробовал это сделать (точнее сделал это во
все узких места, особенно там, где идет работа с БД), и мне показалось это удобным
в плане поиска ошибок и отладке.  

Пример (на логику не обращайте внимания, интересует только проброс исключений):  

       // Получение какого-либо значения из базы данных в виде object
        private object GetData_AsObject()
        {
            try
            {
                //тут идет получение данных из бд
            }
            catch (Exception e)
            {
                throw new Exception("Получение данных из " + tableName + "." + column);
            }
        }

        // Получение какого-либо значения из базы данных в виде string 
        public string GetData_AsString()
        {
            try
            {
                if (GetData_AsObject() != null)
                    return GetData_AsObject().ToString();
                else
                    return "";
            }
            catch (Exception e)
            {
                throw new Exception(e.Message + " as string");
            }
        }

        // Получение какого-либо значения из базы данных в виде int 
        public int GetData_AsInt()
        {
            try
            {
                if (GetData_AsObject() != null)
                    return int.Parse(GetData_AsObject().ToString());
                else
                    return 0;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message + " as int");
            }
        }


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

Но в коде получается очень много конструкций try-catch. Не считается ли это захламлением?
    


Ответы

Ответ 1



Да, это вполне нормальная практика. Чаще всего код не должен заниматься обработкой исключений - у него обычно на это нет соответствующих полномочий. Иными словами, это не его дело. Он должен либо выполнить некие действия, либо, получив исключение, пробросить его вызывающему коду, которому можно доверить эту самую обработку (что само по себе является отдельной задачей, а согласно Single responsibility, первому из принципов SOLID, объект не должен брать на себя более одной обязанности) Бывают, конечно, ситуации, когда исключение является одним из видов "штатной ситуации", и сгенерировавший его код способен корректно его обработать, но это довольно нечастые случаи (и честно говоря, примеры таких ситуаций у меня в голове как-то сразу не всплывают). Более того, тут возникает вопрос - стоит ли в такую ситуацию рассматривать как исключение. Если говорить о вашем примере, то в любом случае логика реакции на исключение должна находиться вне сферы ответственности кода, работающего с БД . Вряд ли этот код должен брать на себя еще и задачу записи в лог или формирования человекопонятного сообщения об ошибке для конечного пользователя. З.Ы. несмотря на ваши слова про "на логику не обращайте внимания", я всё не удержусь и добавлю: искренне надеюсь, следующий кусок не из реального кода? if (GetData_AsObject() != null) return GetData_AsObject().ToString(); else return ""; З.Ы. вы же вроде задавали несколько месяцев тому назад очень похожий вопрос

Ответ 2



Нет, "пробрасыване исключений" в том виде, в котором это делается в коде в вопросе, это не "вполне нормальная практика". Более того, "пробрасывание" именно в таком виде значительно усложняет отладку, делая локализацию ошибок практически невозможной. Исключение стоит ловить в нескольких случаях: Если код, который его ловит, может восстановиться после исключения - корректно продолжнить работу). Например, создать базу данных, если подключение к ней упало с ошибкой "базы данных не существует". Или повторить команду, если первая попытка упала с ошибкой соединения. Исключение ловится и выше не перебрасывается. Если код хочет залоггировать факт исключения на границе слоя. Исключение ловится, логгируется, и перебрасывается через throw;. Если код умышенно хочет скрыть детали исключения, например из соображений безопасности. Оригинальное исключение логгируется, бросается новое исключение, без деталей. В ситуации из вопроса - Если код может добавить дополнительную информацию к оригинальному исключению. Например, дописать tableName и column, как сделано в вопросе. Исключение ловится, бросается новое исключение, с обязательной передачей оригинального исключения как параметра innerException: private object GetData_AsObject() { try { //тут идет получение данных из бд } catch (Exception e) { // передаем e вторым параметром в конструктор Exception throw new Exception("Получение данных из " + tableName + "." + column, e); } } А "пробрасывать" исключения так, как это сделано в коде из вопроса - нельзя, т.к. при этом полностью теряются детали оригинального исключения, и сообщение об ошибке приходит к разработчику в виде "где-то что-то пошло не так, а где именно и что именно - пусть гадает!"

Ответ 3



Я считаю нет смысла пробрасывать Exception. На верхнем уровне сделать try-catch и выводить не e.Message, а e.ToString() - в нем будет и тип исключения и все вложенные функции до исключения, и даже возможно строка кода. А "пробрасывая" так вы всю дополнительную информацию теряете.

Комментариев нет:

Отправить комментарий