Я согласен с постом Стивена Клири в том, что вы должны попытаться классифицировать свое исключение, а затем найти тип исключения, который будет выдан соответствующим образом.Я нахожу категоризацию Эрика Липперта довольно интересной, и во многих отношениях он прав, , но , я думал о другом типе категоризации, который выглядит как классификация Липперта, за исключением того, что я сосредотачиваюсь больше на код контракта .Я предлагаю классифицировать Exceptions
в невыполненных предварительных условиях , постусловиях и простых ошибках .
Дело в том, что каждый методимеет официальный контракт в том смысле, что если вы выполняете все предварительные условия, он обещает удовлетворить некоторые постусловия для вас.Как правило, все предварительные условия можно подразделить на три типа:
- Связанные с безопасностью (уполномочен ли вызывающий абонент выполнять вызов?)
- Связаны с контекстом (находится ли объект справасостояние для совершения вызова?)
- Связанный с вводом (имеет ли вызывающий объект допустимые аргументы?)
В принципе, каждый метод должен проверять эти условия по порядку и выдавать один изследующие типы исключений (точный тип или производная):
SecurityException
InvalidOperationException
ArgumentException
Эти исключения сообщают вызывающей стороне, что «по их вине» что-то пошло не так и вызов не может быть завершен правильно.Однако, если все предварительные условия выполняются в соответствии с формальной спецификацией метода, и метод обнаруживает, что он не может соответствовать указанным постусловиям, он должен выбросить Exception
типа, который четко сообщает , что пошло не так.Как правило, вы хотите определить свой собственный тип исключения для этих ситуаций или повторно использовать существующий тип исключения, если это не один из типов, зарезервированных для сбоев предусловий.
И, наконец, ошибкиExceptions
, которые выдает CLR, которые могут возникать практически в любом месте, в любое время и которые практически невозможно предсказать.Они никогда не будут явным образом частью ваших методов и, как таковые, никогда не должны выбрасываться (ни обрабатываться специально) пользовательским кодом.В этом смысле они сопоставляют почти однозначное с фатальными исключениями Липперта.
Так что вы должны сделать, на мой взгляд, в случае, представленном Слаумой?
Ну, как выМожно себе представить, это полностью зависит от контрактов MyMethod(data)
, GetObject(data)
и objectOfAnotherClass.SomeProperty
.Что говорит API GetObject(data)
о том, в каких ситуациях он вернет null
(или выбросит свои собственные исключения)?Каково точное значение и спецификация SomeProperty
?
Предположим, что GetObject(data)
- это метод, который возвращает объект, извлеченный из базы данных, а data
- это идентификатор объекта.Если в контракте GetObject(data)
указано, что он вернет null
, если не существует объекта с идентификатором data
, то ваш дизайн должен учитывать это в своем собственном контракте.Возможно, вы захотите заставить вызывающего абонента, если это разумно, всегда указывать значение для data
, чтобы GetObject(data)
не возвращало null
, а если это так, вы можете бросить ArgumentException
(или производную), чтобы указатьошибка на стороне вызывающей стороны.
С другой стороны, если GetObject(data)
указывает, что возвращает ноль, только когда у вызывающей стороны недостаточно прав для извлечения объекта, вы можете бросить SecurityException
(или производную) длясообщить о проблеме вызывающему абоненту MyMethod(data)
.
Наконец, если GetObject(data)
обещает, что он "никогда" не вернет null
, и это все равно происходит, вы можете позволить своему коду аварийно завершить работу с NullReferenceException
(потому что справедливо полагая, что это никогда не будет null
) или обрабатывать ситуацию, особенно когда вы имеете дело с чувствительным кодом, бросая свой собственный тип исключения (поскольку постусловие, с точки зрения вызывающей стороны MyMethod, не удалось).
Второй случай немного сложнее, но к нему можно подходить почти так же.Предположим, что objectOfAnotherClass
представляет строку или сущность, которые были извлечены из базы данных по идентификатору data
, а MyMethod(data)
четко указывает, что data
должен указывать идентификатор объекта, где objectOfAnotherClass.SomeProperty >= 0
, тогда в этом случае выдаетсяArgumentException (или производная) будет частью вашего проекта.
Опять же, когда MyMethod(data)
работает в контексте, где вызывающий может предположить, что действительный идентификатор никогда не вернет объект, такой что objectOfAnotherClass.SomeProperty < 0
, (или лучше: даже не подозревает, что такой объект существует), тогда такое появление действительно неожиданно на сайте вызывающего абонента, и MyMethod(data)
не должен даже явно проверять случай (опять же, если предположить, что это не произойдет), или, если будет болеенадежный код, сгенерируйте определенное, пользовательское исключение, указывающее на проблему.
В заключение: я думаю, что определение типа Exception
для выброса зависит исключительно от формальных контрактов, которые каждый метод имеет со своими вызывающими и вызываемыми.Если вызывающий не удовлетворяет предварительному условию, метод должен всегда отвечать вызывающему, бросая SecurityException
, InvalidOperationException
или ArgumentException
.Если сам метод не соответствует его собственным постусловиям, или может произойти сбой исключений, выданных CLR или другими компонентами, или выдать свое собственное более конкретное исключение, указывающее на проблему.