是从属性错误形式引发异常吗?


15

我一直都认为属性(即,它们的设置/获取操作)应该快速/立即且无故障。您永远不必尝试/赶上获取或设置属性。

但是我正在研究将某些对象的属性应用基于角色的安全性的一些方法。例如Employee.Salary属性。我尝试过的一些其他解决方案尝试过的解决方案(尤其是此处的AOP示例)涉及到如果访问者没有正确权限的情况下引发异常-但这违反了我的个人规则很长一段时间了。

所以我问:我错了吗?事情变了吗?是否已接受属性应该能够引发异常?


1
在计算器上同样的问题,得到了更多的关注,因此更好的答案。
罗曼·斯塔科夫

Answers:


14

设置属性的值时,对无效值抛出异常是可以的

获取属性的值应该(几乎)永远不会抛出异常

对于基于角色的访问,请使用不同的/哑接口或立面;不要让人们看到他们无法拥有的东西!


5

我认为这主要是不好的形式,但即使Microsoft有时也建议使用异常。一个很好的例子是抽象属性Stream.Length。按照指导原则,我将更加关注避免对吸气剂产生副作用并限制对二传手的副作用。


4

我绝对会争辩说,如果您觉得需要从属性设置器或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是目前我能想到的唯一有效的异常(无双关语)。


2

我知道您的问题特定于.NET,但是由于C#与Java共享一些历史,所以我认为您可能会感兴趣。我丝毫没有暗示,因为某些事情是用Java完成的,所以应该用C#完成。我知道两者有很大的不同,特别是在C#如何大大改进了对属性的语言级别支持方面。我只是给出一些背景和观点。

根据JavaBeans规范

受约束的属性有时,当发生属性更改时,其他Bean可能希望验证更改,如果不合适,则拒绝更改。我们将经过这种检查的属性称为约束属性。在Java Bean中,需要受约束的属性设置器方法来支持PropertyVetoException。此文档向受约束的属性的用户证明了尝试更新可能被否决。因此,一个简单的受约束属性可能看起来像:

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

请把所有这些和一粒盐一起吃。JavaBeans规范很旧,并且C#属性(IMO)对Java具有的基于“命名约定”的属性进行了巨大的改进。我只是想提供一些背景信息,仅此而已!


这是一个很好的观点,但是setter方法和ac#属性setter之间的区别非常重要,对我来说是不同的情况。同样,Java的检查异常迫使调用代码意识到可以引发异常,因此,此异常捕获任何人的可能性较小。
史蒂文·埃弗斯

2

属性的要点是统一访问原则,即,无论是通过存储还是计算实现,都应该可以通过同一接口访问值。如果您的属性引发表示异常状态的异常,该异常表示程序员无法控制的错误状态(应该捕获并处理的错误类型),您将迫使客户端知道该值是通过计算获得的。

另一方面,我认为使用断言或类似断言的异常不会出现任何问题,这些异常旨在向程序员表示对API的错误使用,而不是被捕获和处理。在这些情况下,从API用户的角度来看,正确的答案不是处理异常(因此隐含地关心值是通过计算还是通过存储来获取)。这是为了修复他/她的代码,以使对象不会最终处于无效状态,或者使您修复代码,从而使断言不会触发。


我看不出如何“强迫您的客户知道通过计算获得的价值”。当然,对存储的访问也会引发异常。实际上,您如何定义两者之间的差异?
Timwi

我认为区别在于,仅从字段(存储)中检索值并对其执行无害的操作(例如将其放在适当类型的变量中(即,不进行强制转换))不会引发异常。在这种情况下,仅当您随后对该值进行了某些操作时,才会发生异常。如果在属性获取器中引发异常,则在那种情况下,简单地检索值并将其放在变量中的行为可能会导致异常。
Wily博士的学徒,2010年

1

AFAIK,此指南主要来自于可能会在设计时使用属性的思考过程。(例如,TextBox上的Text属性)如果设计人员尝试访问该属性时抛出该异常,则VS不会过得很好。尝试使用设计器时会出现很多错误,而对于UI设计器,它们将无法呈现。它也适用于调试时间,尽管您在异常情况下将看到的只是“ xxx Exception”,并且不会破坏VS IIRC。

对于POCO,它实际上不会造成任何伤害,但是我仍然回避自己做。我认为人们会更频繁地访问属性,因此通常它们应该是低成本的。属性不应该做方法的工作,它们应该只获取/设置一些信息,并以此作为一般规则。


0

尽管很大程度上取决于上下文,但我通常会避免将任何类型的易碎逻辑(包括异常)放入属性中。举一个例子,您(或您正在使用的某些库)可以做很多事情,这些事情都需要使用反射来遍历属性,从而导致设计时无法预料的错误。

我通常说的是,虽然有时您可能绝对肯定要在不担心后果的情况下阻止某些事情。我想安全性就是其中的经典案例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.