最佳实践:从属性中抛出异常

Jon*_*gel 102 .net c# properties exception

何时从属性getter或setter中抛出异常是否合适?什么时候不合适?为什么?关于这个主题的外部文件的链接将是有帮助的...谷歌出乎意料地少了.

LBu*_*kin 124

Microsoft在http://msdn.microsoft.com/en-us/library/ms229006.aspx上提供了有关如何设计属性的建议

从本质上讲,他们建议属性getter是轻量级访问器,总是可以安全地调用.如果需要抛出异常,他们建议重新设计getter作为方法.对于setter,他们指出异常是一种适当且可接受的错误处理策略.

对于索引器,Microsoft表示getter和setter都可以抛出异常.事实上,.NET库中的许多索引器都是这样做的.最常见的例外是ArgumentOutOfRangeException.

有一些很好的理由可以解释为什么你不想在属性getter中抛出异常:

  • 因为属性"看起来"是字段,所以它们总是可以抛出(按设计)异常; 而对于方法,程序员接受培训以期望和调查异常是否是调用该方法的预期结果.
  • 许多.NET基础结构都使用getter,比如序列化程序和数据绑定(例如WinForms和WPF) - 在这种情况下处理异常会很快成为问题.
  • 当您观察或检查对象时,调试器会自动评估属性getter.这里的一个例外可能是混乱并减慢您的调试工作.出于同样的原因,在属性中执行其他昂贵的操作(例如访问数据库)也是不合需要的.
  • 属性通常用在链接约定中:obj.PropA.AnotherProp.YetAnother- 使用这种语法,决定在何处注入异常捕获语句会成为问题.

作为旁注,人们应该意识到,仅仅因为某个属性不是为了抛出异常,这并不意味着它不会; 它可以很容易地调用代码.即使是分配新对象(如字符串)的简单操作也可能导致异常.您应始终以防御性方式编写代码,并期望从您调用的任何内容中获得异常.

  • 如果你遇到像"内存不足"这样的致命异常,那么你是否在一个属性或其他地方获得异常并不重要.如果你没有在属性中获取它,那么你只需要在下一个分配内存的东西后几秒钟就可以得到它.问题不是"财产可以抛出异常吗?" 由于致命的情况,几乎所有代码都会抛出异常.问题是财产是否应*按设计*将异常作为其特定合同的一部分. (39认同)
  • @Pavel - 虽然WinForms和WPF都可以从属性访问器中的异常中正常恢复,但从这些错误中识别和恢复并不总是那么容易.在某些情况下,(例如在WPF中,控制模板设置器抛出异常时)会以静默方式吞下异常.如果您之前从未遇到过这种情况,这可能会导致痛苦的调试会话. (6认同)
  • 这些是指导原则而非规则是有原因的; 没有指南涵盖所有疯狂的边缘情况.我可能会自己制作这些方法而不是属性,但这是一个判断调用. (6认同)

Pav*_*aev 34

从setter中抛出异常没有错.毕竟,有什么更好的方法来表明该值对于给定的属性无效?

对于getter来说,它通常是不受欢迎的,并且可以很容易地解释:一般来说,属性getter报告对象的当前状态; 因此,唯一一个吸气器投掷合理的情况是状态无效.但是通常认为设计类是一个好主意,以至于最初无法获得无效对象,或者通过常规方法将其置于无效状态(即始终确保构造函数中的完全初始化,以及尝试使方法在状态有效性和类不变量方面是异常安全的.只要你坚持这个规则,你的财产获取者就不应该陷入他们必须报告无效状态的情况,因此永远不会抛出.

我知道有一个例外,它实际上是一个相当重要的例外:任何实现的对象IDisposable.Dispose特别是用于将对象置于无效状态的方法,并且ObjectDisposedException在这种情况下甚至还有一个特殊的异常类.在处理完对象后,抛出ObjectDisposedException任何类成员(包括属性getter(并排除Dispose自身))是完全正常的.

  • 谢谢帕维尔.这个答案用于"为什么"而不是简单地再次声明从属性中抛出异常并不是一个好主意. (4认同)

Eri*_*ert 24

它几乎不适用于吸气剂,有时适用于定型器.

这些问题的最佳资源是Cwalina和Abrams的"框架设计指南"; 它可作为装订书使用,其中大部分也可在线获取.

从第5.2节:财产设计

避免从属性getter中抛出异常.属性getter应该是简单的操作,不应该有前提条件.如果getter可以抛出异常,则应该将其重新设计为方法.请注意,此规则不适用于索引器,我们确实会在验证参数时遇到异常.

请注意,本指南仅适用于属性获取者.可以在属性设置器中抛出异常.

  • 设计是在面对相互冲突的要求时找到合理妥协的艺术和科学.无论哪种方式似乎都是合理的妥协; 如果物品被丢弃,我不会感到惊讶; 如果没有,我也不会感到惊讶.由于使用处理对象是一种糟糕的编程习惯,因此有任何期望是不明智的. (4认同)
  • 这是如何与一次性对象相关的,以及一旦对象调用了`Dispose()`并且随后要求输出属性值时应该考虑抛出`ObjectDisposedException`的指导?似乎指导应该是"避免从属性getter中抛出异常,除非已经处理了对象,在这种情况下你应该考虑抛出ObjectDisposedExcpetion". (3认同)
  • 虽然(一般而言)我同意这些指导原则,但它认为提供一些额外的见解是有用的,因为它们应该被遵循的原因 - 以及当它们被忽略时会产生什么样的后果. (2认同)