何时从属性getter或setter中抛出异常是否合适?什么时候不合适?为什么?关于这个主题的外部文件的链接会有所帮助......谷歌出人意料地很少。
答案 0 :(得分:121)
Microsoft提供了有关如何在http://msdn.microsoft.com/en-us/library/ms229006.aspx
设计属性的建议基本上,他们建议属性getter是轻量级访问器,总是可以安全地调用。如果需要抛出异常,他们建议重新设计getter作为方法。对于setter,他们指出异常是一种适当且可接受的错误处理策略。
对于索引器,Microsoft表示getter和setter都可以抛出异常。事实上,.NET库中的许多索引器都是这样做的。最常见的例外是ArgumentOutOfRangeException
。
有一些很好的理由可以解释为什么你不想在属性getter中抛出异常:
obj.PropA.AnotherProp.YetAnother
- 使用这种语法决定在哪里注入异常捕获语句会成为问题。作为旁注,人们应该知道,仅仅因为某个属性未设计来抛出异常,这并不意味着它不会;它可以很容易地调用代码。即使是分配新对象(如字符串)的简单操作也可能导致异常。您应始终以防御性方式编写代码,并期望从您调用的任何内容中获得异常。
答案 1 :(得分:34)
从setter中抛出异常没有错。毕竟,有什么更好的方法来表明该值对于给定的属性无效?
对于吸气剂,它通常是不受欢迎的,并且可以很容易地解释:一般来说,属性吸气剂报告物体的当前状态;因此,唯一一个吸气器投掷合理的情况是状态无效。但是通常认为设计类是一个好主意,以至于最初无法获得无效对象,或者通过常规方式将其置于无效状态(即始终确保构造函数中的完全初始化,以及尝试使方法在状态有效性和类不变量方面是异常安全的。只要你坚持这个规则,你的财产获取者就不应该陷入他们必须报告无效状态的情况,因此永远不会抛出。
我知道有一个例外,它实际上是一个相当重要的例外:任何实现IDisposable
的对象。 Dispose
专门用于将对象置于无效状态,并且在这种情况下甚至可以使用特殊的异常类ObjectDisposedException
。在处理完对象后,从任何类成员抛出ObjectDisposedException
是完全正常的,包括属性getter(并排除Dispose
本身)。
答案 2 :(得分:24)
它几乎不适用于吸气剂,有时适用于定型器。
这些问题的最佳资源是Cwalina和Abrams的“框架设计指南”;它可作为装订书使用,其中大部分也可在线获取。
从第5.2节:物业设计
避免抛出异常 财产瘾君子。物业吸气剂 应该是简单的操作和应该 没有先决条件。如果是一个吸气剂 它应该抛出异常 可能会被重新设计成一种方法。 请注意,此规则不适用于 索引器,我们期待的地方 验证的例外情况 争论。
请注意,本指南仅适用 财产吸气剂。抛出是可以的 属性设置器中的异常。
答案 3 :(得分:2)
Exceptions的一个不错的方法是使用它们为自己和其他开发人员记录代码,如下所示:
异常应该是特殊的程序状态。这意味着可以随时随地写下它们!
您可能希望将它们放入getter中的一个原因是记录类的API - 如果软件在程序员尝试使用它时错误地抛出异常,那么它们就不会错误地使用它!例如,如果您在数据读取过程中进行了验证,那么如果数据中存在致命错误,则可能无法继续并访问该过程的结果。在这种情况下,如果出现错误,您可能希望获取输出抛出,以确保其他程序员检查此情况。
它们是记录子系统/方法/任何事物的假设和边界的一种方式。在一般情况下,他们不应被抓住!这也是因为如果系统以预期的方式一起工作,它们永远不会被抛出:如果发生异常则表明不满足一段代码的假设 - 例如它不会与周围的世界交互它原本打算用于。如果您捕获为此目的而编写的异常,则可能意味着系统已进入不可预测/不一致的状态 - 这可能最终导致数据或类似的崩溃或损坏,这可能更难以检测/调试。 / p>
异常消息是报告错误的一种非常粗略的方式 - 它们无法集中收集并且只包含字符串。这使它们不适合报告输入数据中的问题。在正常运行中,系统本身不应进入错误状态。因此,它们中的消息应该为程序员而不是用户设计 - 输入数据中出错的东西可以被发现并以更合适的(自定义)格式传递给用户。
此规则的例外情况(哈哈!)就像IO一样,例外情况不在您的控制之下,无法提前检查。
答案 4 :(得分:1)
这些都记录在MSDN中(与其他答案相关联)但这是一般的经验法则......
在setter中,如果您的属性应在上面和下面进行验证。例如,一个名为PhoneNumber的属性应该具有正则表达式验证,如果格式无效,则应该抛出错误。
对于getter,可能在值为null时,但很可能是您希望在调用代码上处理的内容(根据设计指南)。
答案 5 :(得分:0)
MSDN:捕获和投掷标准异常类型
答案 6 :(得分:0)
这是一个非常复杂的问题和答案取决于您的对象的使用方式。根据经验,属于“后期绑定”的属性获取者和设置者不应该抛出异常,而具有“早期绑定”的属性应该在需要时抛出异常。顺便说一句,微软的代码分析工具在我看来过于狭隘地定义了属性的使用。
“后期绑定”意味着通过反射找到属性。例如,Serializeable“属性用于通过其属性对对象进行序列化/反序列化。在这种情况下抛出异常会以灾难性方式破坏事物,并且不是使用异常来生成更强大代码的好方法。” p>
“早期绑定”意味着编译器在代码中绑定了属性使用。例如,当您编写的某些代码引用属性getter时。在这种情况下,可以在有意义时抛出异常。
具有内部属性的对象具有由这些属性的值确定的状态。表示对对象内部状态有意识且敏感的属性的属性不应用于后期绑定。例如,假设您有一个必须打开,访问,然后关闭的对象。在这种情况下,在不先调用open的情况下访问属性应该会导致异常。假设,在这种情况下,我们不抛出异常,我们允许代码访问值而不抛出异常?代码看起来很开心,即使它从一个无意义的getter获得了一个值。现在我们已经将调用getter的代码置于糟糕的情况,因为它必须知道如何检查值以查看它是否无意义。这意味着代码必须假设它从属性getter获得的值才能验证它。这就是编写错误代码的方式。
答案 7 :(得分:0)
我有这个代码,我不确定要抛出哪个异常。
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);
}
我通过强制它在构造函数中作为参数来防止模型首先使属性为null。
{{1}}