Вызывает ли исключение из свойства плохую форму?

15

Я всегда придерживался мнения, что свойства (т. Е. Их операции set / get) должны быть быстрыми / немедленными и безошибочными. Вы никогда не должны пытаться поймать или установить собственность.

Но я смотрю на некоторые способы применения безопасности на основе ролей к свойствам некоторых объектов. Например, свойство Employee.Salary. Некоторые из решений, с которыми я столкнулся, которые пробовали другие (в частности, пример AOP здесь ), включают создание исключения, если средство доступа не имеет необходимых разрешений - но это идет вразрез с личным правилом, которое у меня было в течение длительного времени.

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

Стивен Эверс
источник
1
Этот же вопрос о StackOverflow привлек больше внимания и, таким образом, получил лучшие ответы.
Роман Старков

Ответы:

14

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

получение значения свойства не должно (почти) никогда генерировать исключение

для доступа на основе ролей используйте разные / более тупые интерфейсы или фасады; не позволяйте людям видеть то, что они не могут иметь!

Стивен А. Лоу
источник
5

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

ChaosPandion
источник
4

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

Свойство - это абстракция, представляющая нечто, представляющее собой просто значение . И вы должны иметь возможность устанавливать значение, не опасаясь, что это может вызвать исключение. *

Если установка свойства приводит к побочному эффекту, то это должно быть реализовано как метод. И если это не приводит к каким-либо побочным эффектам, то не должно быть никаких исключений.

Одним из примеров, уже упомянутых в другом ответе, является Stream.Positionсвойство. Это вызывает побочные эффекты и может создавать исключения. Но этот установщик свойств - просто оболочка, Stream.Seekкоторую вы можете вызвать вместо этого.

Лично я считаю, что позиция не должна была быть записываемой собственностью.

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

public class User {
    public string Email {
        get { return _email; }
        set { 
            if (!IsValidEmail(value)) throw InvalidEmailException(value);
            _email = value;
        }
    }

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

public class Email {
    public Email(string value) {
        if (!IsValidEmail(value)) throw new InvalidEmailException(value);
        ...
    }
    ...
}

public class User {
    public Email Email { get; set; }
}

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

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

* ObjectDisposedException - единственное допустимое исключение (без каламбура), о котором я могу думать в данный момент.

Пит
источник
2

Я знаю, что ваш вопрос относится к .NET , но поскольку C # делится историей с Java, я подумал, что вас это может заинтересовать. Я не в коем случае , подразумевая , что , потому что что - то делается в Java, это должно быть сделано в C #. Я знаю, что они очень разные, особенно в том, что в C # значительно улучшена поддержка свойств на уровне языка. Я просто даю некоторый контекст и перспективу.

Из спецификации JavaBeans :

Ограниченные свойства Иногда, когда происходит изменение свойства, другой компонент может захотеть проверить изменение и отклонить его, если оно неуместно. Мы ссылаемся на свойства, которые проходят такую ​​проверку, как на ограниченные свойства. В Java Beans для установки PropertyVetoException требуются методы установки ограниченных свойств. Это документы для пользователей ограниченного свойства, что попытки обновления могут быть наложены вето. Таким образом, простое ограниченное свойство может выглядеть так:

PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

Возьми все это с крошкой соли, пожалуйста. Спецификация JavaBeans устарела, а свойства C # (IMO) - огромное улучшение по сравнению со свойствами, основанными на «соглашении об именовании», которые есть в Java. Я просто пытаюсь дать немного контекста, вот и все!

Майк Кларк
источник
Это хороший момент, но различие между методом сеттера и сеттером свойства ac ​​# является достаточно значительным для меня. Аналогично, проверенные исключения Java заставляют вызывающий код осознавать, что исключение может быть выдано, поэтому меньше шансов, что это кого-нибудь застигнет врасплох.
Стивен Эверс
2

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

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

dsimcha
источник
Я не понимаю, как «вы заставляете своего клиента знать, что значение получается с помощью вычислений». Конечно, доступ к хранилищу тоже может вызвать исключение. На самом деле, как бы вы определили разницу между ними?
Тимви
Я считаю, что различие заключается в том, что простое извлечение значения из поля (хранилища) и выполнение чего-то безобидного с ним, например, помещение его в переменную соответствующего типа (т.е. без приведения), не может вызвать исключение. В этом случае исключение может произойти только в том случае, если вы впоследствии что-то сделали со значением. Если исключение выдается в получателе свойства, то в этом случае простое извлечение значения и помещение его в переменную может вызвать исключение.
Ученик доктора Вилли
1

AFAIK, это руководство главным образом исходит из мыслительного процесса, что свойства могут в конечном итоге использоваться во время разработки . (Например, свойство Text в TextBox) Если свойство бросает исключение, когда дизайнер пытается получить к нему доступ, у VS не будет хорошего дня. Вы получите кучу ошибок, просто пытаясь использовать конструктор, а для дизайнеров пользовательского интерфейса они не будут отображаться. Это также относится и ко времени отладки, хотя в сценарии исключений вы увидите просто «Исключение xxx», и оно не сработает VS IIRC.

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

MIA
источник
0

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

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

FinnNk
источник