Когда уместно генерировать исключение из получателя или установщика свойства? Когда это не подходит? Зачем? Ссылки на внешние документы по этой теме были бы полезны ... Google оказался на удивление мало.
c#
.net
exception
properties
Джон Сейгел
источник
источник
Ответы:
У Microsoft есть свои рекомендации по проектированию свойств на http://msdn.microsoft.com/en-us/library/ms229006.aspx.
По сути, они рекомендуют, чтобы получатели свойств были облегченными средствами доступа, которые всегда можно безопасно вызывать. Они рекомендуют переделывать геттеры в методы, если вам нужно выбросить исключения. Для установщиков они указывают, что исключения являются подходящей и приемлемой стратегией обработки ошибок.
Для индексаторов Microsoft указывает, что как геттеры, так и сеттеры допустимо генерировать исключения. Фактически, это делают многие индексаторы в библиотеке .NET. Наиболее частым исключением является
ArgumentOutOfRangeException
.Есть несколько довольно веских причин, по которым вы не хотите создавать исключения в методах получения свойств:
obj.PropA.AnotherProp.YetAnother
- при таком синтаксисе становится проблематичным решить, куда вводить операторы исключения исключения.В качестве примечания следует помнить, что то, что свойство не предназначено для создания исключения, не означает, что этого не произойдет; это может легко вызвать код, который это делает. Даже простое выделение нового объекта (например, строки) может привести к исключениям. Вы всегда должны писать свой код с осторожностью и ожидать исключений от всего, что вы вызываете.
источник
Нет ничего плохого в том, чтобы генерировать исключения из сеттеров. В конце концов, какой лучший способ указать, что значение недействительно для данного свойства?
Для геттеров это обычно не одобряется, и это можно довольно легко объяснить: геттер свойства, как правило, сообщает текущее состояние объекта; таким образом, единственный случай, когда геттер имеет смысл выбросить, - это когда состояние недействительно. Но также обычно считается хорошей идеей проектировать свои классы таким образом, чтобы просто невозможно было изначально получить недопустимый объект или перевести его в недопустимое состояние обычными средствами (т.е. всегда обеспечивать полную инициализацию в конструкторах и попробуйте сделать методы безопасными в отношении исключений в отношении допустимости состояния и инвариантов класса). Пока вы придерживаетесь этого правила, ваши геттеры никогда не должны попадать в ситуацию, когда они должны сообщать о недопустимом состоянии и, следовательно, никогда не бросать.
Есть одно известное мне исключение, и оно на самом деле довольно серьезное: реализация любого объекта
IDisposable
.Dispose
специально предназначен как способ привести объект в недопустимое состояние, и в этом случае даже есть специальный класс исключенияObjectDisposedException
. Совершенно нормально бросатьObjectDisposedException
из любого члена класса, включая средства получения свойств (и исключая егоDispose
самого), после того, как объект был удален.источник
IDisposable
должны стать бесполезными после aDispose
. Если для вызова члена потребуется использовать ресурс, которыйDispose
стал недоступным (например, член будет читать данные из потока, который был закрыт), член должен выдавать,ObjectDisposedException
а не утечку, напримерArgumentException
, но если у кого-то есть форма со свойствами, которые представляют значений в определенных полях, было бы гораздо полезнее разрешить чтение таких свойств после удаления (с получением последних введенных значений), чем требовать ...Dispose
должно быть отложено до тех пор, пока не будут прочитаны все такие свойства. В некоторых случаях, когда один поток может использовать блокирующее чтение объекта, в то время как другой его закрывает, и когда данные могут поступать в любое время доDispose
этого, может быть полезноDispose
отключить входящие данные, но разрешить чтение ранее полученных данных. Не следует навязывать искусственное различие между ситуациямиClose
иDispose
в тех случаях, когда в противном случае ничего не существовало бы.Get...
метод. Исключением является ситуация, когда вам необходимо реализовать существующий интерфейс, который требует от вас предоставления свойства.Это почти никогда не подходит для геттера, а иногда подходит для сеттера.
Лучшим источником ответов на подобные вопросы является «Руководство по разработке каркаса» Квалины и Абрамса; он доступен в виде переплетенной книги, и большие его части также доступны в Интернете.
Из раздела 5.2: Дизайн недвижимости
источник
ObjectDisposedException
после того, как объект былDispose()
вызван и что-то впоследствии запрашивает значение свойства? Похоже, что руководство должно быть таким: «Избегайте генерирования исключений из методов получения свойств, если только объект не был удален, и в этом случае вам следует рассмотреть возможность создания исключения ObjectDisposedExcpetion».Один из хороших подходов к Исключениям - использовать их для документирования кода для себя и других разработчиков следующим образом:
Исключения должны быть для исключительных состояний программы. Это значит, что их можно писать где угодно!
Одна из причин, по которой вы можете захотеть поместить их в геттеры, - это документировать API класса - если программное обеспечение выдает исключение, как только программист пытается использовать его неправильно, он не будет использовать его неправильно! Например, если у вас есть проверка во время процесса чтения данных, может не иметь смысла иметь возможность продолжить и получить доступ к результатам процесса, если в данных были фатальные ошибки. В этом случае вы можете захотеть получить выходной бросок, если были ошибки, чтобы гарантировать, что другой программист проверит это условие.
Это способ документировать предположения и границы подсистемы / метода / чего угодно. В общем случае ловить их не следует! Это также связано с тем, что они никогда не выбрасываются, если система работает вместе ожидаемым образом: если происходит исключение, оно показывает, что предположения фрагмента кода не выполняются - например, он не взаимодействует с окружающим миром таким образом изначально это было предназначено. Если вы поймаете исключение, которое было написано для этой цели, это, вероятно, означает, что система вошла в непредсказуемое / несогласованное состояние - это может в конечном итоге привести к сбою или повреждению данных или тому подобному, что, вероятно, будет намного сложнее обнаружить / отладить.
Сообщения об исключениях - это очень грубый способ сообщения об ошибках - они не могут собираться массово и действительно содержат только строку. Это делает их непригодными для сообщения о проблемах во входных данных. При нормальной работе сама система не должна переходить в состояние ошибки. В результате сообщения в них должны быть предназначены для программистов, а не для пользователей - ошибки во входных данных могут быть обнаружены и переданы пользователям в более подходящих (настраиваемых) форматах.
Исключение (ха-ха!) Из этого правила - это такие вещи, как ввод-вывод, где исключения не находятся под вашим контролем и не могут быть проверены заранее.
источник
Все это задокументировано в MSDN (как указано в других ответах), но вот общее практическое правило ...
В установщике, если ваше свойство должно быть проверено выше и выше типа. Например, свойство с именем PhoneNumber, вероятно, должно иметь проверку регулярного выражения и должно выдавать ошибку, если формат недействителен.
Для геттеров, возможно, когда значение равно null, но, скорее всего, это то, что вы захотите обработать в вызывающем коде (в соответствии с рекомендациями по дизайну).
источник
MSDN: перехват и выброс стандартных типов исключений
http://msdn.microsoft.com/en-us/library/ms229007.aspx
источник
Это очень сложный вопрос, и ответ зависит от того, как используется ваш объект. Как показывает практика, методы получения и установки свойств, которые являются «поздним связыванием», не должны генерировать исключения, в то время как свойства с исключительно «ранним связыванием» должны генерировать исключения, когда возникает необходимость. Кстати, инструмент анализа кода Microsoft, на мой взгляд, слишком узко определяет использование свойств.
"позднее связывание" означает, что свойства обнаруживаются посредством отражения. Например, атрибут «Serializeable» используется для сериализации / десериализации объекта с помощью его свойств. Вызов исключения во время такого рода ситуации приводит к катастрофическим сбоям в работе и не является хорошим способом использования исключений для создания более надежного кода.
«раннее связывание» означает, что использование свойства ограничено в коде компилятором. Например, когда некоторый код, который вы пишете, ссылается на средство получения свойства. В этом случае можно создавать исключения, когда они имеют смысл.
Состояние объекта с внутренними атрибутами определяется значениями этих атрибутов. Свойства, выражающие атрибуты, которые осведомлены о внутреннем состоянии объекта и чувствительны к нему, не должны использоваться для позднего связывания. Например, допустим, у вас есть объект, который нужно открыть, получить к нему доступ, а затем закрыть. В этом случае доступ к свойствам без предварительного вызова open должен привести к исключению. Предположим в этом случае, что мы не генерируем исключение и разрешаем коду доступ к значению, не генерируя исключения? Код будет казаться счастливым, даже если он получил значение от бессмысленного получателя. Теперь мы поместили код, который вызывал геттер, в плохую ситуацию, поскольку он должен знать, как проверить значение, чтобы убедиться, что оно не имеет смысла. Это означает, что код должен делать предположения о значении, которое он получил от средства получения свойства, чтобы его проверить. Так пишется плохой код.
источник
У меня был этот код, в котором я не был уверен, какое исключение нужно выбросить.
Я запретил модели иметь свойство равным нулю в первую очередь, заставив его в качестве аргумента в конструкторе.
источник