Когда бросить исключение? - PullRequest
400 голосов
/ 17 сентября 2008

У меня есть исключения, созданные для каждого условия, которое мое приложение не ожидает. UserNameNotValidException, PasswordNotCorrectException и т. Д.

Однако мне сказали, что я не должен создавать исключения для этих условий. В моем UML эти ARE являются исключениями для основного потока, так почему бы не быть исключением?

Любое руководство или лучшие практики для создания исключений?

Ответы [ 33 ]

1 голос
/ 17 сентября 2008

У меня есть три типа условий, которые я ловлю.

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

  2. AppException. Обычно это исключение, которое вы обнаруживаете и добавляете в свой код. Другими словами, это те, которые вы ожидаете (файл не существует). Зарегистрируйте его, установите сообщение и вернитесь на страницу с общей ошибкой. На этой странице обычно есть немного информации о том, что произошло.

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

Надеюсь, это поможет

1 голос
/ 06 октября 2011

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

1 голос
/ 17 сентября 2008

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

В статье ниже утверждается, что исключение составляют исключительные условия и ошибки.

Неправильное имя пользователя - это не обязательно ошибка программы, а ошибка пользователя ...

Вот достойная отправная точка для исключений в .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx

1 голос
/ 24 июня 2010

На мой взгляд, фундаментальный вопрос должен заключаться в том, следует ли ожидать, что вызывающая сторона захочет продолжить нормальный поток программ, если возникает условие. Если вы не знаете, либо у вас есть отдельные методы doSomething и trySomething, где первый возвращает ошибку, а второй нет, либо есть подпрограмма, которая принимает параметр, указывающий, следует ли генерировать исключение в случае сбоя). Рассмотрим класс для отправки команд в удаленную систему и отчета об ответах. Определенные команды (например, перезапуск) заставят удаленную систему отправить ответ, но затем не будут отвечать на запросы в течение определенного периода времени. Таким образом, полезно иметь возможность отправить команду «ping» и выяснить, отвечает ли удаленная система в течение разумного промежутка времени без необходимости генерировать исключение, если это не так (вызывающая сторона, вероятно, ожидает, что первые несколько » Попытки пинга не увенчались успехом, но в конечном итоге все получилось С другой стороны, если есть последовательность команд вроде:

  exchange_command("open tempfile");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("close tempfile");
  exchange_command("copy tempfile to realfile");

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

На самом деле, в приведенном выше сценарии может быть полезно иметь параметр для выбора ряда режимов обработки ошибок: никогда не генерировать исключения, генерировать исключения только для ошибок связи или генерировать исключения в любых случаях, когда команда не возвращает индикация «успеха».

1 голос
/ 17 сентября 2008

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

В вашем случае, если вызывается такой метод, как Login (имя пользователя, пароль), если имя пользователя недопустимо, действительно правильно выбросить исключение UserNameNotValidException или PasswordNotCorrectException, если пароль неверен. Пользователь не может войти в систему, используя предоставленные параметры (т. Е. Это невозможно, потому что это нарушит аутентификацию), поэтому выведите исключение. Хотя, возможно, ваши два исключения унаследованы от ArgumentException.

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

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

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

Вот пример того, как избежать использования Исключений в сценарии, как только что описано, с помощью шаблона Try ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}
0 голосов
/ 23 апреля 2014

Вот мои предложения:

Я не думаю, что это ВСЕГДА хороший способ вызвать исключение, потому что для обработки таких исключений потребуется больше времени, памяти.

На мой взгляд, если что-то можно обработать «добрым, вежливым» способом (это означает, что если мы можем «предсказать такие ошибки, используя if …… или что-то в этом роде), мы должны ИЗБЕЖАТЬ используя» исключения «но просто верните флаг, как« false », с внешним значением параметра, сообщающим ему / ей подробную причину.

Например, мы можем создать класс, подобный следующему:

public class ValueReturnWithInfo<T>
{
   public T Value{get;private set;}
   public string errorMsg{get;private set;}
   public ValueReturnWithInfo(T value,string errmsg)
   {
      Value = value;
      errMsg = errmsg;
   }
}

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

Однако, обратите внимание, что , если некоторые ошибки не могут быть описаны так легко (это зависит от вашего опыта программирования) с «если» …… (например, исключения FileIO), вы должны выбросить исключения .

0 голосов
/ 22 марта 2011

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

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

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

Итак, как правило:

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

0 голосов
/ 24 октября 2008

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

0 голосов
/ 17 сентября 2008

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

  • Кто звонит на ваш код? Это какой-то общедоступный API или внутренняя библиотека?
  • Какой язык вы используете? Если это, например, Java, то генерирование (проверенного) исключения накладывает явное бремя на вашего вызывающего, чтобы каким-то образом обрабатывать это состояние ошибки, а не возвращать статус, который можно игнорировать. Это может быть хорошо или плохо.
  • Как обрабатываются другие ошибки в том же приложении? Вызывающие абоненты не хотят иметь дело с модулем, который обрабатывает ошибки уникальным способом, в отличие от чего-либо еще в системе.
  • Сколько вещей может пойти не так с рассматриваемой рутиной, и как они будут обрабатываться по-другому? Рассмотрим разницу между серией блоков перехвата, которые обрабатывают различные ошибки, и переключением на код ошибки.
  • У вас есть структурированная информация об ошибке, которую нужно вернуть? Создание исключения дает вам лучшее место для размещения этой информации, чем просто возвращение статуса.
0 голосов
/ 17 сентября 2008

Вы можете использовать несколько общих исключений для этих условий. Например, ArgumentException предназначен для использования, когда что-то не так с параметрами в методе (за исключением ArgumentNullException). Обычно вам не нужны исключения, такие как LessThanZeroException, NotPrimeNumberException и т. Д. Подумайте о пользователе вашего метода. Число условий, которые она хочет обработать специально, равно количеству типов исключений, которые должен выдавать ваш метод. Таким образом, вы можете определить, насколько подробными будут ваши исключения.

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

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