[Из песочницы] Как правильно создавать пользовательские исключения в C#

Для кого написана статья Данная статья предназначена прежде всего для новичков в мире .NET, но может быть полезна также и разработчикам с опытом, которые не до конца разобрались, как правильно строить свои user-defined exceptions с помощью C#.Пример кода для данной статьи можно скачать здесь.

Создание простого исключения Создавать собственные типы исключений в C# рекомендуется в тех случаях, когда нужно четко отделить возникшую в написанном программистом коде исключительную ситуацию, от исключения, возникающего в стандартных типах .NET Framework.К примеру, есть метод, призванный изменять имя пользователя: private static void EditUser (string oldUserName, string newUserName) { var userForEdit = GetUserByName (oldUserName); if (userForEdit == null) return; else userForEdit.Name = newUserName; } Данный метод будет решать возложенные на него задачи, но не будет делать одну важную вещь — он не сообщит о том, что пользователь с заданным именем отсутствует, и операция смены его имени не была выполнена.

Чтобы проинформировать о возникшей исключительной ситуации можно генерировать стандартное исключение, что не является рекомендуемой практикой:

private static void EditUser (string oldUserNane, string newUserName) { var userForEdit = GetUserByName (oldUserName); if (userForEdit == null) throw new Exception (); else userForEdit.Name = newUserName; } Для того чтобы можно было легко определить, что исключение генерируется на уровне конкретного приложения, нужно создать свой — пользовательский Exception, и при получении null вместо нужного пользователя выбрасывать именно его.

Создать свой Exception не сложно — нужно определить public-класс, который будет наследоваться от System.Exception или System.ApplicationException. Хотя это и не является хорошей практикой, кода внутри созданного класса исключения можно не писать вообще:

public class UserNotFoundException: ApplicationException { } От чего лучше наследоваться, от System.Exception или от System.ApplicationException? Каждый из этих типов предназначен для конкретной цели. Тогда как System.Exception является общим классом для всех user-defined exceptions, то System.ApplicationException определяет исключения, возникающие на уровне конкретного приложения.К примеру, тестовое приложения из данной статьи является отдельной программой, поэтому вполне допустимо наследовать определенный нами exception от System.ApplicationException.

Теперь вместо Exception мы сгенерируем созданный нами UserNotFoundException:

private static void EditUser (string oldUserNane, string newUserName) { var userForEdit = GetUserByName (oldUserName); if (userForEdit == null) throw new UserNotFoundException (); else userForEdit.Name = newUserName; } В таком случае в качестве сообщения о возникшем исключении будет: «Error in the application.». Что не очень информативно.

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

класс исключения должен наследоваться от Exception/ApplicationException; класс должен быть помечен атрибутом [System.Serializable]; класс должен определять стандартный конструктор; класс должен определять конструктор, который устанавливает значение унаследованного свойства Message; класс должен определять конструктор для обработки «внутренних исключений»; класс должен определять конструктор для поддержки сериализации типа. Дабы избавить программиста от необходимости писать одинаковый код в Visual Studio есть сниппет «Exception», который генерирует класс исключения, соответствующий всем рекомендациям, перечисленным выше.

f40aac3a7949e2fa7079899bcc2dd393.png

Итак, после воплощения рекомендаций в жизнь, код нашего исключения должен выглядеть примерно так:

public class UserNotFoundException: ApplicationException { public UserNotFoundException () { }

public UserNotFoundException (string message) : base (message) { }

public UserNotFoundException (string message, Exception inner) : base (message, inner) { }

protected UserNotFoundException (SerializationInfo info, StreamingContext context) : base (info, context) { } } Теперь при генерации исключения мы можем указать причину его возникновения более подробно:

throw new UserNotFoundException («User \» + oldUserName + »\» not found in system»); И последний штрих — добавление в XML-документацию (если вы, конечно, ее используете) нашего метода информации о том, что он может выбросить исключение определенного типа:

/// Итак, наш user-defined exception готов к применению. Вы можете добавить к нему все что душе угодно: дополнительные поля, описывающие состояние исключения, содержащие дополнительную информацию и т.д.

© Habrahabr.ru