Рекомендации: исключение из свойств

Когда целесообразно выбрасывать исключение из getter или setter свойства? Когда это не подходит? Зачем? Ссылки на внешние документы по этому вопросу были бы полезны... Google оказался на удивление мало.

Ответ 1

У Microsoft есть рекомендации по проектированию свойств в http://msdn.microsoft.com/en-us/library/ms229006.aspx

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

Для индексировщиков Microsoft указывает, что для геттеров и сеттеров приемлемо допускать исключения. И на самом деле, многие индексы в библиотеке .NET делают это. Наиболее распространенным исключением является ArgumentOutOfRangeException.

Есть несколько довольно веских причин, по которым вы не хотите генерировать исключения в свойствах getters:

  • Поскольку свойства "появляются" как поля, не всегда очевидно, что они могут генерировать (по-дизайн) исключение; тогда как с помощью методов программисты обучаются ожидать и исследовать, являются ли исключения ожидаемым следствием вызова метода.
  • Getters используются многими инфраструктурами .NET, такими как сериализаторы и привязка данных (например, в WinForms и WPF). Обработка исключений в таких контекстах может стать проблематичной.
  • Получатели свойств автоматически оцениваются отладчиками при просмотре или проверке объекта. Исключение здесь может привести к запутыванию и замедлению ваших усилий по отладке. Также нежелательно выполнять другие дорогостоящие операции в свойствах (например, доступ к базе данных) по тем же причинам.
  • Свойства часто используются в соглашении о цепочке: obj.PropA.AnotherProp.YetAnother - с таким синтаксисом становится проблематичным решать, куда вводить инструкции исключения catch.

В качестве побочного примечания следует помнить, что только потому, что свойство не предназначено для исключения исключения, это не значит, что оно не будет; он может легко вызвать код, который делает. Даже простой акт выделения нового объекта (например, строки) может привести к исключениям. Вы всегда должны защищать свой код и ожидать исключения из всего, что вы вызываете.

Ответ 2

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

Для геттеров это обычно неодобрительно, и это можно объяснить довольно легко: свойство getter, в общем, сообщает текущее состояние объекта; таким образом, единственный случай, когда разумно выбрасывать геттер, - это когда состояние недействительно. Но также считается хорошей идеей для разработки ваших классов таким образом, что изначально невозможно получить недопустимый объект или перевести его в недействительное состояние с помощью обычных средств (т.е. Всегда обеспечивать полную инициализацию в конструкторах и попробуйте сделать методы безопасными для исключения по отношению к государственной действительности и инвариантам класса). До тех пор, пока вы придерживаетесь этого правила, ваши получатели собственности никогда не должны попадать в ситуацию, когда они должны сообщать о некорректном состоянии и, следовательно, никогда не бросать.

Есть одно исключение, о котором я знаю, и это на самом деле довольно важное: любой объект, реализующий IDisposable. Dispose специально предназначен как способ привести объект в недопустимое состояние, и в этом случае будет использоваться специальный класс исключения ObjectDisposedException. Совершенно нормально бросать ObjectDisposedException из любого члена класса, включая свойства getters (и исключая Dispose), после того, как объект был удален.

Ответ 3

Это почти никогда не подходит для геттера, а иногда и подходит для сеттера.

Наилучшим ресурсом для таких вопросов является "Руководство по разработке каркаса" от Cwalina и Abrams; он доступен как связанная книга, а большие его части также доступны в Интернете.

Из раздела 5.2: Property Design

ИЗБЕГАЙТЕ исключения из имущество геттеров. Недвижимость должны быть простыми операциями и должны не имеют предварительных условий. Если геттер может вызвать исключение, оно должно вероятно, будет изменен как метод. Обратите внимание, что это правило не применяется к индексаторов, где мы ожидаем исключения в результате проверки аргументы.

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

Ответ 4

Все это задокументировано в MSDN (как указано в других ответах), но здесь общее правило...

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

Для getters, возможно, когда значение равно null, но, скорее всего, это то, что вы хотите обработать на вызывающем коде (в соответствии с руководством по дизайну).

Ответ 5

Один хороший подход к Исключениям - использовать их для документирования кода для себя и других разработчиков следующим образом:

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

Одна из причин, по которой вы можете захотеть поместить их в getters, - это документировать API класса - если программное обеспечение выдает исключение, как только программист пытается использовать его неправильно, они не будут использовать его неправильно! Например, если у вас есть проверка во время процесса чтения данных, может быть бессмысленно иметь возможность продолжить и получить доступ к результатам процесса, если в данных произошли фатальные ошибки. В этом случае вы можете захотеть получить выходной бросок, если есть ошибки, чтобы убедиться, что другой программист проверяет это условие.

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

Сообщения об исключении - очень грубый способ сообщения об ошибках - они не могут быть собраны en-masse и содержат только строку. Это делает их непригодными для сообщения о проблемах во входных данных. При нормальной работе сама система не должна вводить состояние ошибки. В результате этого сообщения в них должны быть предназначены для программистов, а не для пользователей. Ошибки во входных данных могут быть обнаружены и переданы пользователям в более подходящих (настраиваемых) форматах.

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

Ответ 7

Это очень сложный вопрос, и ответ зависит от того, как используется ваш объект. Как правило, свойства getters и seters, которые являются "поздними связями", не должны генерировать исключения, тогда как свойства с исключительно "ранним связыванием" должны генерировать исключения, когда возникает такая необходимость. BTW, инструмент анализа кода Microsoft, по моему мнению, слишком ограниченно использует свойства.

"поздняя привязка" означает, что свойства найдены через отражение. Например, атрибут Serializeable используется для сериализации/десериализации объекта через его свойства. Бросание исключения во время такого рода ситуаций разрушает ситуацию катастрофическим образом и не является хорошим способом использования исключений для создания более надежного кода.

"раннее связывание" означает, что использование свойства связано с кодом компилятором. Например, когда код, который вы пишете, ссылается на свойство getter. В этом случае нормально делать исключения, когда они имеют смысл.

Объект с внутренними атрибутами имеет состояние, определяемое значениями этих атрибутов. Свойства, выражающие атрибуты, которые осведомлены и чувствительны к внутреннему состоянию объекта, не должны использоваться для позднего связывания. Например, скажем, у вас есть объект, который нужно открыть, получить доступ, а затем закрыть. В этом случае доступ к свойствам без вызова open сначала должен привести к исключению. Предположим, в этом случае мы не генерируем исключение, и мы разрешаем коду доступ к значению без исключения исключения? Код будет казаться счастливым, даже если он получил значение от геттера, который не имеет смысла. Теперь мы поместили код, который вызвал геттер в плохую ситуацию, так как он должен знать, как проверить значение, чтобы увидеть, не является ли оно бессмысленным. Это означает, что код должен делать предположения о значении, полученном им из свойства getter, чтобы его проверить. Вот как записывается плохой код.

Ответ 8

У меня был этот код, в котором я не знал, какое исключение выбрано.

public Person
{
    public string Name { get; set; }
    public boolean HasPets { get; set; }
}

public void Foo(Person person)
{
    if (person.Name == null) {
        throw new Exception("Name of person is null.");
        // I was unsure of which exception to throw here.
    }

    Console.WriteLine("Name is: " + person.Name);
}

Я помешал модели иметь свойство null в первую очередь, заставив его в качестве аргумента в конструкторе.

public Person
{
    public Person(string name)
    {
        if (name == null) {
            throw new ArgumentNullException(nameof(name));
        }
        Name = name;
    }

    public string Name { get; private set; }
    public boolean HasPets { get; set; }
}

public void Foo(Person person)
{
    Console.WriteLine("Name is: " + person.Name);
}