我一直都认为属性(即,它们的设置/获取操作)应该快速/立即且无故障。您永远不必尝试/赶上获取或设置属性。
但是我正在研究将某些对象的属性应用基于角色的安全性的一些方法。例如Employee.Salary属性。我尝试过的一些其他解决方案尝试过的解决方案(尤其是此处的AOP示例)涉及到如果访问者没有正确权限的情况下引发异常-但这违反了我的个人规则很长一段时间了。
所以我问:我错了吗?事情变了吗?是否已接受属性应该能够引发异常?
我一直都认为属性(即,它们的设置/获取操作)应该快速/立即且无故障。您永远不必尝试/赶上获取或设置属性。
但是我正在研究将某些对象的属性应用基于角色的安全性的一些方法。例如Employee.Salary属性。我尝试过的一些其他解决方案尝试过的解决方案(尤其是此处的AOP示例)涉及到如果访问者没有正确权限的情况下引发异常-但这违反了我的个人规则很长一段时间了。
所以我问:我错了吗?事情变了吗?是否已接受属性应该能够引发异常?
Answers:
我认为这主要是不好的形式,但即使Microsoft有时也建议使用异常。一个很好的例子是抽象属性Stream.Length。按照指导原则,我将更加关注避免对吸气剂产生副作用并限制对二传手的副作用。
我绝对会争辩说,如果您觉得需要从属性设置器或getter引发异常,则设计中存在缺陷。
属性是一种抽象,表示仅是值的某物。而且您应该能够设置一个值,而不必担心这样做会引发异常。*
如果设置该属性导致副作用,则应将其真正实现为方法。而且,如果它不产生任何副作用,则不应抛出任何异常。
在另一个答案中已经提到的一个示例是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是目前我能想到的唯一有效的异常(无双关语)。
我知道您的问题特定于.NET,但是由于C#与Java共享一些历史,所以我认为您可能会感兴趣。我丝毫没有暗示,因为某些事情是用Java完成的,所以应该用C#完成。我知道两者有很大的不同,特别是在C#如何大大改进了对属性的语言级别支持方面。我只是给出一些背景和观点。
根据JavaBeans规范:
受约束的属性有时,当发生属性更改时,其他Bean可能希望验证更改,如果不合适,则拒绝更改。我们将经过这种检查的属性称为约束属性。在Java Bean中,需要受约束的属性设置器方法来支持PropertyVetoException。此文档向受约束的属性的用户证明了尝试更新可能被否决。因此,一个简单的受约束属性可能看起来像:
PropertyType getFoo(); void setFoo(PropertyType value) throws PropertyVetoException;
请把所有这些和一粒盐一起吃。JavaBeans规范很旧,并且C#属性(IMO)对Java具有的基于“命名约定”的属性进行了巨大的改进。我只是想提供一些背景信息,仅此而已!
属性的要点是统一访问原则,即,无论是通过存储还是计算实现,都应该可以通过同一接口访问值。如果您的属性引发表示异常状态的异常,该异常表示程序员无法控制的错误状态(应该捕获并处理的错误类型),您将迫使客户端知道该值是通过计算获得的。
另一方面,我认为使用断言或类似断言的异常不会出现任何问题,这些异常旨在向程序员表示对API的错误使用,而不是被捕获和处理。在这些情况下,从API用户的角度来看,正确的答案不是处理异常(因此隐含地关心值是通过计算还是通过存储来获取)。这是为了修复他/她的代码,以使对象不会最终处于无效状态,或者使您修复代码,从而使断言不会触发。