C# 最佳实践:从属性抛出异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1488472/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best practices: throwing exceptions from properties
提问by Jon Seigel
When is it appropriate to throw an exception from within a property getter or setter? When is it not appropriate? Why? Links to external documents on the subject would be helpful... Google turned up surprisingly little.
什么时候从属性 getter 或 setter 中抛出异常合适?什么时候不合适?为什么?指向有关该主题的外部文档的链接会有所帮助... Google 的搜索结果出奇地少。
采纳答案by LBushkin
Microsoft has its recommendations on how to design properties at http://msdn.microsoft.com/en-us/library/ms229006.aspx
Microsoft 在http://msdn.microsoft.com/en-us/library/ms229006.aspx上有关于如何设计属性的建议
Essentially, they recommend that property getters be lightweight accessors that are always safe to call. They recommend redesigning getters to be methods if exceptions are something you need to throw. For setters they indicate that exceptions are an appropriate and acceptable error handling strategy.
本质上,他们建议属性 getter 是轻量级的访问器,它们总是可以安全调用。如果您需要抛出异常,他们建议将 getter 重新设计为方法。对于 setter,它们表明异常是一种适当且可接受的错误处理策略。
For indexers, Microsoft indicates that it is acceptable for both getters and setters to throw exceptions. And in fact, many indexers in the .NET library do this. The most common exception being ArgumentOutOfRangeException
.
对于索引器,Microsoft 表示 getter 和 setter 都可以抛出异常。事实上,.NET 库中的许多索引器都是这样做的。最常见的例外是ArgumentOutOfRangeException
.
There are some pretty good reasons why you don't want to throw exceptions in property getters:
您不想在属性 getter 中抛出异常有一些很好的理由:
- Because properties "appear" to be fields, it is not always apparent that they can throw a (by-design) exception; whereas with methods, programmers are trained to expect and investigate whether exceptions are an expected consequence of invoking the method.
- Getters are used by a lot of .NET infrastructure, like serializers and databinding (in WinForms and WPF for example) - dealing with exceptions in such contexts can rapidly become problematic.
- Property getters are automatically evaluated by debuggers when you watch or inspect an object. An exception here can be confusing and slow down your debugging efforts. It's also undesirable to perform other expensive operations in properties (like accessing a database) for the same reasons.
- Properties are often used in a chaining convention:
obj.PropA.AnotherProp.YetAnother
- with this kind of syntax it becomes problematic to decide where to inject exception catch statements.
- 因为属性“似乎”是字段,所以它们可以抛出(按设计)异常并不总是很明显;而对于方法,程序员被训练去预期和调查异常是否是调用方法的预期结果。
- 很多 .NET 基础设施都使用了 Getter,例如序列化程序和数据绑定(例如在 WinForms 和 WPF 中)——处理此类上下文中的异常很快就会出现问题。
- 当您观察或检查对象时,调试器会自动评估属性 getter。这里的例外可能会令人困惑并减慢您的调试工作。出于同样的原因,在属性中执行其他昂贵的操作(如访问数据库)也是不可取的。
- 属性通常用于链式约定:
obj.PropA.AnotherProp.YetAnother
- 使用这种语法,决定在哪里注入异常捕获语句变得有问题。
As a side note, one should be aware that just because a property is not designedto throw an exception, that doesn't mean it won't; it could easily be calling code that does. Even the simple act of allocating a new object (like a string) could result in exceptions. You should always write your code defensively and expect exceptions from anything you invoke.
作为旁注,人们应该意识到,仅仅因为属性不是为了抛出异常而设计的,这并不意味着它不会;它可以很容易地调用这样做的代码。即使是分配新对象(如字符串)的简单操作也可能导致异常。您应该始终防御性地编写代码,并期望从您调用的任何内容中获得异常。
回答by smack0007
MSDN: Catching and Throwing Standard Exception Types
MSDN:捕获和抛出标准异常类型
回答by Eric Lippert
It is almost never appropriate on a getter, and sometimes appropriate on a setter.
它几乎不适合用于 getter,有时也适合用于 setter。
The best resource for these sorts of questions is "Framework Design Guidelines" by Cwalina and Abrams; it's available as a bound book, and large portions of it are also available online.
这类问题的最佳资源是 Cwalina 和 Abrams 的“框架设计指南”;它可以作为装订书使用,其中很大一部分也可以在线获得。
From section 5.2: Property Design
来自第 5.2 节:物业设计
AVOID throwing exceptions from property getters. Property getters should be simple operations and should not have preconditions. If a getter can throw an exception, it should probably be redesigned to be a method. Note that this rule does not apply to indexers, where we do expect exceptions as a result of validating the arguments.
Note that this guideline only applies to property getters. It is OK to throw an exception in a property setter.
避免从属性 getter 中抛出异常。属性 getter 应该是简单的操作,不应该有先决条件。如果 getter 可以抛出异常,则可能应该将其重新设计为方法。请注意,此规则不适用于索引器,我们确实希望在验证参数时会出现异常。
请注意,本指南仅适用于属性 getter。在属性设置器中抛出异常是可以的。
回答by David
This is all documented in MSDN (as linked to in other answers) but here is a general rule of thumb...
这都记录在 MSDN 中(链接到其他答案),但这里有一个一般的经验法则......
In the setter, if your property should be validated above and beyond type. For example, a property called PhoneNumber should probably have regex validation and should throw an error if the format is not valid.
在 setter 中,如果您的属性应该在类型之外进行验证。例如,名为 PhoneNumber 的属性可能应该具有正则表达式验证,并且如果格式无效则应该抛出错误。
For getters, possibly when the value is null, but most likely that is something you will want to handle on the calling code (per the design guidelines).
对于 getter,可能当值为 null 时,但很可能这是您想要在调用代码上处理的事情(根据设计指南)。
回答by Pavel Minaev
There's nothing wrong with throwing exceptions from setters. After all, what better way to indicate that the value is not valid for a given property?
从 setter 抛出异常并没有错。毕竟,有什么更好的方法来表明该值对于给定的属性无效?
For getters, it is generally frowned upon, and that can be explained pretty easily: a property getter, in general, reports the current state of an object; thus, the only case where it is reasonable for a getter to throw is when the state is invalid. But it is also generally considered to be a good idea to design your classes such that it is simply not possible to get an invalid object initially, or to put it into invalid state via normal means (i.e., always ensure full initialization in constructors, and try make methods exception-safe with respect to state validity and class invariants). So long as you stick to that rule, your property getters should never get into a situation where they have to report invalid state, and thus never throw.
对于 getter,它通常是不受欢迎的,这可以很容易地解释:属性 getter 通常报告对象的当前状态;因此,getter 抛出的唯一合理情况是状态无效时。但通常也认为设计类是一个好主意,使得最初不可能获得无效对象,或通过正常方式将其置于无效状态(即,始终确保在构造函数中完全初始化,并且尝试使方法在状态有效性和类不变量方面是异常安全的)。只要您坚持该规则,您的属性获取者就永远不会陷入必须报告无效状态的情况,因此永远不会抛出。
There is one exception I know of, and it's actually a rather major one: any object implementing IDisposable
. Dispose
is specifically intended as a way to bring object into an invalid state, and there's even a special exception class, ObjectDisposedException
, to be used in that case. It is perfectly normal to throw ObjectDisposedException
from any class member, including property getters (and excluding Dispose
itself), after the object has been disposed.
我知道有一个例外,它实际上是一个相当重要的例外:任何实现IDisposable
. Dispose
专门用作使对象进入无效状态的一种方式,ObjectDisposedException
在这种情况下甚至可以使用一个特殊的异常类。在对象被处理后ObjectDisposedException
,从任何类成员抛出是完全正常的,包括属性 getter(并排除Dispose
自身)。
回答by JonnyRaa
One nice approach to Exceptions is to use them to document code for yourself and other developers as follows:
异常的一种很好的方法是使用它们为您自己和其他开发人员记录代码,如下所示:
Exceptions should be for exceptional program states. This means it's fine to write them wherever you want!
例外应该是针对特殊的程序状态。这意味着可以将它们写在任何你想要的地方!
One reason you might want to put them in getters is to document the API of a class - if the software throws an exception as soon as a programmer tries to use it wrong then they wont use it wrong! For instance if you have validation during a data reading process it may not make sense to be able to continue and access the results of the process if there were fatal errors in the data. In this case you may want to make getting the output throw if there were errors to ensure that another programmer checks for this condition.
您可能想要将它们放入 getter 的一个原因是记录类的 API - 如果软件在程序员尝试错误使用它时立即抛出异常,那么他们不会错误地使用它!例如,如果您在数据读取过程中进行了验证,如果数据中存在致命错误,那么继续并访问该过程的结果可能没有意义。在这种情况下,如果出现错误,您可能希望让输出抛出,以确保另一个程序员检查这种情况。
They are a way of documenting the assumptions and boundaries of a subsystem/method/whatever. In the general case they should not be caught! This is also because they are never thrown if the system is working together in the way expected: If an exception happens it shows that the assumptions of a piece of code are not met - eg it is not interacting with the world around it in the way it was originally intended to. If you catch an exception that was written for this purpose it probably means the system has entered an unpredictable/inconsistent state - this may ultimately lead to a crash or corruption of data or similar which is likely to be much harder to detect/debug.
它们是记录子系统/方法/任何东西的假设和边界的一种方式。在一般情况下,他们不应该被抓住!这也是因为如果系统以预期的方式一起工作,它们永远不会被抛出:如果发生异常,它表明一段代码的假设没有得到满足 - 例如它没有以这种方式与周围的世界进行交互它最初是打算这样做的。如果您捕获为此目的而编写的异常,则可能意味着系统已进入不可预测/不一致的状态 - 这可能最终导致数据或类似的崩溃或损坏,这可能更难检测/调试。
Exception messages are a very coarse way of reporting errors - they cannot be collected en-masse and only really contain a string. This makes them unsuitable for reporting problems in input data. In normal running the system itself should not enter an error state. As a result of this the messages in them should be designed for programmers and not for users - things that are wrong in input data can be discovered and relayed to users in more suitable (custom) formats.
异常消息是一种非常粗略的错误报告方式——它们不能被整体收集,并且只真正包含一个字符串。这使得它们不适合报告输入数据中的问题。在正常运行中,系统本身不应进入错误状态。因此,它们中的消息应该是为程序员设计的,而不是为用户设计的——输入数据中的错误可以被发现并以更合适的(自定义)格式转发给用户。
The Exception (haha!) to this rule is things like IO where exceptions are not under your control and cannot be checked for in advance.
这条规则的例外(哈哈!)是像 IO 这样的事情,其中例外不受您的控制并且无法提前检查。
回答by Hyman D Menendez
This is a very complex question and answer depends on how your object is used. As a rule of thumb, property getters and setters that are "late binding" should not throw exceptions, while properties with exclusively "early binding" should throw exceptions when the need arises. BTW, Microsoft's code analysis tool is defining the use of properties too narrowly in my opinion.
这是一个非常复杂的问题,答案取决于您的对象的使用方式。根据经验,“后期绑定”的属性 getter 和 setter 不应抛出异常,而专门具有“早期绑定”的属性应在需要时抛出异常。顺便说一句,在我看来,微软的代码分析工具对属性的使用定义过于狭隘。
"late binding" means that properties are found through reflection. For example the Serializeable" attribute is used to serialize/deserialize an object via its properties. Throwing an exception during in this kind of situation breaks things in a catastrophic way and is not a good way of using exceptions to make more robust code.
“后期绑定”意味着通过反射找到属性。例如,Serializeable 属性用于通过其属性序列化/反序列化对象。在这种情况下抛出异常会以灾难性的方式破坏事物,并且不是使用异常来制作更健壮代码的好方法。
"early binding" means that a property use is bound in the code by the compiler. For example when some code that you write references a property getter. In this case it is OK to throw exceptions when they make sense.
“早期绑定”意味着编译器在代码中绑定了属性使用。例如,当您编写的某些代码引用属性 getter 时。在这种情况下,可以在有意义的时候抛出异常。
An object with internal attributes has a state determined by the values of those attributes. Properties expressing attributes that are aware and sensitive to the object's internal state should not be used for late binding. For example, lets say you have an object that must be opened, accessed, then closed. In this case accessing the properties without calling open first should result in an exception. Suppose, in this case, that we do not throw an exception and we allow the code access to a value without throwing an exception? The code will seem happy even though it got a value from a getter that is non-sense. Now we have put the code that called the getter in a bad situation since it must know how to check the value to see if it is non-sense. This means that the code must make assumptions about the value that it got from the property getter in order to validate it. This is how bad code gets written.
具有内部属性的对象具有由这些属性的值确定的状态。表达对对象内部状态敏感和敏感的属性的属性不应用于后期绑定。例如,假设您有一个必须打开、访问然后关闭的对象。在这种情况下,访问属性而不先调用 open 应该会导致异常。假设,在这种情况下,我们不抛出异常并且我们允许代码访问一个值而不抛出异常?即使它从一个没有意义的 getter 那里得到了一个值,代码看起来也会很开心。现在我们已经将调用 getter 的代码置于糟糕的情况下,因为它必须知道如何检查值以查看它是否无意义。这意味着代码必须对其从属性 getter 获得的值做出假设,以便对其进行验证。这就是编写糟糕代码的方式。
回答by Fred
I had this code where I was unsure of which exception to throw.
我有这段代码,我不确定要抛出哪个异常。
public Person
{
public string Name { get; set; }
public boolean HasPets { get; set; }
}
public void Foo(Person person)
{
if (person.Name == null) {
throw new Exception("Name of person is null.");
// I was unsure of which exception to throw here.
}
Console.WriteLine("Name is: " + person.Name);
}
I prevented the model from having the property being null in the first place by forcing it as an argument in the constructor.
我通过强制将其作为构造函数中的参数来阻止模型首先将该属性设为 null。
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);
}