C# 避免空引用异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1943465/
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
avoiding null reference exceptions
提问by Nippysaurus
Apparently the vast majority of errors in code are null reference exceptions. Are there any general techniques to avoid encountering null reference errors?
显然,代码中的绝大多数错误都是空引用异常。是否有任何通用技术可以避免遇到空引用错误?
Unless I am mistaken, I am aware that in languages such as F# is it not possible to have a null value. But thats not the question, I'm asking how to avoid null reference errors in languages such as C#.
除非我弄错了,否则我知道在 F# 等语言中不可能有空值。但这不是问题,我问的是如何避免 C# 等语言中的空引用错误。
采纳答案by David Allen
When a null reference exception is displayed to the user, this indicates a defect in the code resulting from an error on the part of the developer. Here are some ideas on how to prevent these errors.
当向用户显示空引用异常时,这表明由于开发人员的错误而导致的代码缺陷。以下是有关如何防止这些错误的一些想法。
My top recommendation for people who care about software quality, and are also using the.net programming platform, is to install and use Microsoft code contracts ( http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx). It includes capabilities to do run-time checking and static verification. The essential capability to build these contracts into your code is being included in the 4.0 version of the.net framework. If you are interested in code quality, and it sounds like you are, you may really enjoy using Microsoft code contracts.
对于关心软件质量并且也在使用 .net 编程平台的人,我的最高建议是安装和使用 Microsoft 代码合同 ( http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx) . 它包括执行运行时检查和静态验证的功能。将这些合约构建到您的代码中的基本功能已包含在 .net 框架的 4.0 版本中。如果您对代码质量感兴趣,而且听起来您很感兴趣,那么您可能真的很喜欢使用 Microsoft 代码合同。
With Microsoft code contracts, you can guard your method from null values by adding preconditions like this "Contract.Requires(customer != null);". Adding a precondition like this is equivalent to the practice recommended by many others in their comments above. Prior to code contracts, I would have recommended you do something like this
使用 Microsoft 代码契约,您可以通过添加像“Contract.Requires(customer != null);”这样的先决条件来保护您的方法免受空值的影响。添加这样的先决条件相当于许多其他人在上面的评论中推荐的做法。在代码合同之前,我会建议你做这样的事情
if (customer == null) {throw new ArgumentNullException("customer");}
Now I recommend
现在我推荐
Contract.Requires(customer != null);
You can then enable the run-time checking system which will catch these defects as early as possible, leading you towards diagnosis and correction of the defective code. But don't let me give you the impression that code contracts are simply a fancy way to replace argument null exceptions. They are much more powerful than that. With Microsoft code contracts, you can also run the static checker, and ask it to investigate possible sites in your code where null reference exceptions might occur. The static checker requires a bit more experience to use easily. I would not recommend it first for beginners. But feel free to try it out and see for yourself.
然后您可以启用运行时检查系统,该系统将尽早发现这些缺陷,引导您诊断和纠正有缺陷的代码。但是不要让我给你一种印象,即代码契约只是一种替换参数空异常的奇特方式。他们比那要强大得多。使用 Microsoft 代码合同,您还可以运行静态检查器,并要求它调查代码中可能发生空引用异常的可能站点。静态检查器需要更多经验才能轻松使用。我不会首先向初学者推荐它。但请随意尝试并亲眼看看。
RESEARCH ON THE PREVALENCE OF NULL REFERENCE ERRORS
对 NULL 引用错误普遍性的研究
There has been some debate in this thread on whether null reference errors are a significant problem. A long-winded answer is below. For people who don't want to wade through that, I will summarize.
关于空引用错误是否是一个重大问题,该线程中存在一些争论。一个冗长的答案如下。对于那些不想涉足的人,我会总结一下。
- Microsoft's leading researchers in program correctness on the Spec# and code contracts projects believe it is a problem worth addressing.
- Dr. Bertrand Meyer and the team of software engineers at ISE, who developed and support the Eiffel programming language, also believe it is a problem worth addressing.
- In my own commercial experience developing ordinary software, I have seen null reference errors often enough, that I would like to address the problem in my own products and practices.
- Microsoft 在 Spec# 和代码合同项目的程序正确性方面的领先研究人员认为这是一个值得解决的问题。
- Bertrand Meyer 博士和 ISE 开发和支持 Eiffel 编程语言的软件工程师团队也认为这是一个值得解决的问题。
- 在我自己开发普通软件的商业经验中,我经常看到空引用错误,我想在我自己的产品和实践中解决这个问题。
For years, Microsoft has invested in research designed to improve software quality. One of their efforts was the Spec# project. One of the most exciting developments in my opinion with the.net 4.0 framework, is the introduction of Microsoft code contracts, which is an outgrowth of the earlier work done by the Spec# research team.
多年来,微软一直投资于旨在提高软件质量的研究。他们的努力之一是 Spec# 项目。在我看来,.net 4.0 框架最令人兴奋的发展之一是引入了 Microsoft 代码合同,这是 Spec# 研究团队早期工作的产物。
Regarding your remark "the vast majority of errors in code are null reference exceptions", I believe it is the qualifier "the vast majority" that will cause some disagreements. The phrase "Vast majority" suggests that perhaps 70-90% of faults have a null reference exception as the root cause. This seems far too high to me. I prefer to quote from the research of the Microsoft Spec#. In their article The Spec# programming system: An overview, by Mike Barnett, K. Rustan M. Leino, and Wolfram Schulte. In CASSIS 2004, LNCS vol. 3362, Springer, 2004, they wrote
关于您的评论“代码中的绝大多数错误是空引用异常”,我认为是限定词“绝大多数”会引起一些分歧。短语“绝大多数”表明,可能 70-90% 的错误都将空引用异常作为根本原因。这对我来说似乎太高了。我更喜欢引用 Microsoft Spec# 的研究。在他们的文章 The Spec# 编程系统:概述中,作者是 Mike Barnett、K. Rustan M. Leino 和 Wolfram Schulte。在 CASSIS 2004 中,LNCS 卷。3362, Springer, 2004, 他们写道
1.0 Non-Null Types Many errors in modern programs manifest themselves as null-dereference errors, suggesting the importance of a programming language providing the ability to discriminate between expressions that may evaluate to null and those that are sure not to (for some experimental evidence, see [24, 22]). In fact, we would like to eradicate all null dereference errors.
1.0 非空类型现代程序中的许多错误都表现为空引用错误,这表明编程语言提供区分可能评估为空的表达式和确定不为空的表达式的能力的重要性(对于一些实验证据,见 [24, 22])。事实上,我们希望消除所有空引用错误。
This is a likely source for people at Microsoft who are familiar with this research. This article is available at the Spec# site.
对于熟悉这项研究的 Microsoft 人员来说,这可能是一个来源。本文可在 Spec# 站点上找到。
I've copied references 22 and 24 below, and included the ISBN for your convenience.
我已经复制了下面的参考文献 22 和 24,为了您的方便,还包括了 ISBN。
Manuel Fahndrich and K. Rustan M. Leino. Declaring and checking non-null types in an object-oriented language. In Proceedings of the 2003 ACM Conference on Object-Oriented Programming, Systems, Languages, and Applications, OOPSLA 2003, volume 38, number 11 in SIGPLAN Notices, pages 302–312. ACM, November 2003. isbn = {1-58113-712-5},
Cormac Flanagan, K. Rustan M. Leino, Mark Lillibridge, Greg Nelson, James B. Saxe, and Raymie Stata. Extended static checking for Java. In Proceedings of the 2002 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), volume 37, number 5 in SIGPLAN Notices, pages 234–245. ACM, May 2002.
Manuel Fahndrich 和 K. Rustan M. Leino。在面向对象的语言中声明和检查非空类型。在 2003 年 ACM 面向对象编程、系统、语言和应用程序会议的会议记录中,OOPSLA 2003,第 38 卷,SIGPLAN 通知第 11 号,第 302-312 页。ACM,2003 年 11 月。 isbn = {1-58113-712-5},
Cormac Flanagan、K. Rustan M. Leino、Mark Lillibridge、Greg Nelson、James B. Saxe 和 Raymie Stata。Java 的扩展静态检查。在 2002 年 ACM SIGPLAN 编程语言设计和实现 (PLDI) 会议的会议记录中,第 37 卷,SIGPLAN 通知第 5 号,第 234-245 页。ACM,2002 年 5 月。
I reviewed these references. The first reference indicates some experiments they did reviewing their own code for possible null reference defects. Not only did they find several, but in many cases, the identification of a potential null reference indicated a broader problem with the design.
我回顾了这些参考资料。第一个参考表明他们确实了自己的代码以寻找可能的空参考缺陷的一些实验。他们不仅发现了几个,而且在许多情况下,潜在的空引用的识别表明设计存在更广泛的问题。
The second reference does not provide any specific evidence for the assertion that null reference errors are problem. But the authors do state that in their experience, these null reference errors are significant source of software defects. The paper then proceeds to explain how they try to eradicate these defects.
第二个引用没有为空引用错误是问题的断言提供任何具体证据。但作者确实指出,根据他们的经验,这些空引用错误是软件缺陷的重要来源。然后,论文继续解释他们如何试图消除这些缺陷。
I also remembered seeing something about this in an announcement from ISE on a recent release of Eiffel. They refer to this issue as "void safety", and like so many things inspired or developed by Dr. Bertrand Meyer, they have an eloquent and educational description of the problem and how they go about preventing it in their language and tools. I recommend you read their article http://doc.eiffel.com/book/method/void-safety-background-definition-and-toolsto learn more.
我还记得在 ISE 关于最近发布的 Eiffel 的公告中看到了一些关于此的信息。他们将这个问题称为“无效安全”,就像 Bertrand Meyer 博士启发或开发的许多东西一样,他们对这个问题以及他们如何用他们的语言和工具来防止它进行了雄辩和有教育意义的描述。我建议您阅读他们的文章 http://doc.eiffel.com/book/method/void-safety-background-definition-and-tools以了解更多信息。
If you want to learn more about Microsoft code contracts, there are tons of articles that have arisen recently. You can also check my blog at http: SLASH SLASH codecontracts.info which is primarily devoted to conversations about software quality through the use of programming with contracts.
如果您想了解有关 Microsoft 代码合同的更多信息,最近出现了大量文章。您还可以在 http: SLASH SLASH codecontracts.info 上查看我的博客,该博客主要致力于通过使用合同编程来讨论软件质量。
回答by Preet Sangha
One way is to use the Null Value Objects (aka the Null Object Pattern) where possible. There are more details here
回答by Jeremy Raymond
Really if in your language there are null values, it's bound to happen. The null reference errors come from errors in application logic - so unless you can avoid all of those your're bound to hit some.
真的,如果在您的语言中存在空值,它肯定会发生。空引用错误来自应用程序逻辑中的错误 - 因此,除非您可以避免所有这些错误,否则您一定会遇到一些错误。
回答by Andy West
Appropriate use of structured exception handling can help avoid such errors.
适当使用结构化异常处理有助于避免此类错误。
Also, unit testing can help you make sure your code behaves as expected, including ensuring that values are not null when they're not supposed to be.
此外,单元测试可以帮助您确保代码按预期运行,包括确保值在不应该为空时不为空。
回答by Brian Agnew
Using Null Object Patternsis key here.
使用空对象模式是这里的关键。
Make sure that you require collections to be empty in the case when they're not populated, rather than null. Using a null collection when an empty collection would do is confusing and often unnecessary.
确保在未填充的情况下要求集合为空,而不是 null。使用空集合时使用空集合会令人困惑并且通常是不必要的。
Finally, I make my objects assert for non-null values upon construction wherever possible. That way I'm in no doubt later on as to whether values are null, and only have to perform null checks where essential. For most of my fields and parameters I can assume that values are not null based on previous assertions.
最后,我尽可能让我的对象在构造时为非空值断言。这样一来,我对值是否为空毫无疑问,并且只需要在必要的地方执行空检查。对于我的大多数字段和参数,我可以假设基于先前的断言值不为空。
回答by Jim Schubert
One of the most common null reference errors that I've seen is from strings. There will be a check:
我见过的最常见的空引用错误之一是来自字符串。会有一个检查:
if(stringValue == "") {}
But, the string is really null. It should be:
但是,该字符串确实为空。它应该是:
if(string.IsNullOrEmpty(stringValue){}
Also, you could be overly cautious and check an object isn't null before you attempt to access members/methods of that object.
此外,您可能过于谨慎并在尝试访问该对象的成员/方法之前检查该对象是否为空。
回答by Guffa
You can easily check for a null reference before it causes an exception, but usually that is not the real problem, so you would just end up throwing an exception anyway as the code can't really continue without any data.
您可以在导致异常之前轻松检查空引用,但这通常不是真正的问题,因此无论如何您最终都会抛出异常,因为没有任何数据,代码无法真正继续。
Often the main problem isn't the fact that you have a null reference, but that you got a null reference in the first place. If a reference is not supposed to be null, you shouldn't get past the point where the reference is initialised without having a proper reference.
通常,主要问题不在于您有一个空引用,而是您首先获得了一个空引用。如果引用不应该为空,则您不应该在没有正确引用的情况下超过引用初始化的点。
回答by Brian
You don't.
你没有。
Or rather, there's nothing special to do to try to 'prevent' NREs in C#. For the most part an NRE is just some type of logic error. You can firewall these off at interface boundaries by checking parameters and having lots of code like
或者更确切地说,没有什么特别的事情可以尝试在 C# 中“防止”NRE。在大多数情况下,NRE 只是某种类型的逻辑错误。您可以通过检查参数并使用大量代码在接口边界处将这些防火墙关闭
void Foo(Something x) {
if (x==null)
throw new ArgumentNullException("x");
...
}
all over the place (much of the .Net Framework does this), so that when you do screw up, you get a slightly more informative diagnostic (the stack trace is even more valuable, though, and an NRE provides that too). But you still just end up with an exception.
到处都是(大部分 .Net 框架都这样做),所以当你搞砸时,你会得到一个稍微多一点的诊断信息(不过,堆栈跟踪甚至更有价值,NRE 也提供了这一点)。但你最终还是有一个例外。
(Aside: Exceptions like these - NullReferenceException, ArgumentNullException, ArgumentException, ... - typically should not be caught by the program, but rather just means "developer of this code, there is a bug, please fix it". I refer to these as a 'design time' exceptions; contrast these with true 'run time' exceptions that happen as a result of the run time environment (e.g. FileNotFound) and are intended to potentially be caught and handled by the program.)
(旁白:像这样的异常 - NullReferenceException, ArgumentNullException, ArgumentException, ... - 通常不应该被程序捕获,而只是意味着“这段代码的开发者,有一个错误,请修复它”。我指的是这些作为“设计时”异常;将这些与由于运行时环境(例如 FileNotFound)而发生的真正的“运行时”异常进行对比,并可能被程序捕获和处理。)
But at the end of the day, you just have to code it right.
但归根结底,您只需要正确编码即可。
Ideally the majority of NREs would never happen because 'null' is a nonsensical value for many types/variables, and ideally the static type system would disallow 'null' as a value for those particular types/variables. Then the compiler would prevent you from introducing this type of accidental error (ruling out certain classes of errors are what compilers and type systems are best at). This is where certain languages and type systems excel.
理想情况下,大多数 NRE 永远不会发生,因为 'null' 是许多类型/变量的无意义值,理想情况下,静态类型系统不允许将 'null' 作为这些特定类型/变量的值。然后编译器会阻止你引入这种类型的意外错误(排除某些类别的错误是编译器和类型系统最擅长的)。这是某些语言和类型系统擅长的地方。
But without those features, you just test your code to ensure you don't have code paths with this type of error (or possibly use some outside tools that can do extra analysis for you).
但是如果没有这些功能,您只需测试您的代码以确保您没有出现此类错误的代码路径(或者可能使用一些可以为您进行额外分析的外部工具)。
回答by richj
In addition to the above (Null Objects, Empty Collections), there are some general techniques, namely Resource Acquisition is Initialization (RAII) from C++ and Design By Contract from Eiffel. These boil down to:
除了上述(Null Objects、Empty Collections)之外,还有一些通用技术,即C++的Resource Acquisition is Initialization (RAII)和Eiffel的Design By Contract。这些归结为:
- Initialize variables with valid values.
- If a variable can be null, then either check for null and treat it as a special case or expect a null reference exception (and deal with that). Assertions can be used to test for contract violations in development builds.
- 使用有效值初始化变量。
- 如果变量可以为空,则要么检查空值并将其视为特殊情况,要么期望空引用异常(并处理该异常)。断言可用于测试开发版本中的合同违规情况。
I've seen a lot of code that looks like this:
我见过很多类似这样的代码:
if ((value != null) && (value.getProperty() != null) && ... && (...doSomethingUseful())
if ((value != null) && (value.getProperty() != null) && ... && (...doSomethingUseful())
A lot of the time this is completely unnecessary and most of the tests could be removed with stricter initialization and tighter contract definitions.
很多时候这是完全没有必要的,大多数测试可以通过更严格的初始化和更严格的合同定义来删除。
If this is a problem in your code base then it is necessary to understand in each case what the null represents:
如果这是您的代码库中的问题,那么有必要在每种情况下了解 null 代表什么:
- If the null represents an empty collection, use an empty collection.
- If the null represents an exceptional case, throw an Exception.
- If the null represents an accidentally uninitialized value, explicitly initialize it.
- If the null represents a legitimate value, test for it - or even better use a NullObject that performs a null op.
- 如果 null 表示空集合,则使用空集合。
- 如果 null 表示异常情况,则抛出异常。
- 如果 null 表示意外未初始化的值,请显式初始化它。
- 如果 null 表示合法值,请对其进行测试 - 或者甚至更好地使用执行 null 操作的 NullObject。
In practice this standard of clarity at the design level is non-trivial and requires effort and self-discipline to apply consistently to your code base.
在实践中,这种设计级别的清晰标准非常重要,需要努力和自律才能始终如一地应用于您的代码库。
回答by Mark Simpson
One of the simplest ways to avoid NullReferenceExceptions is to aggressively check for null references in your class constructors/methods/property setters and draw attention to the problem.
避免 NullReferenceExceptions 的最简单方法之一是积极检查类构造函数/方法/属性设置器中的空引用并引起对问题的注意。
E.g.
例如
public MyClass
{
private ISomeDependency m_dependencyThatWillBeUsedMuchLater
// passing a null ref here will cause
// an exception with a meaningful stack trace
public MyClass(ISomeDependency dependency)
{
if(dependency == null) throw new ArgumentNullException("dependency");
m_dependencyThatWillBeUsedMuchLater = dependency;
}
// Used later by some other code, resulting in a NullRef
public ISomeDependency Dep { get; private set; }
}
In the above code, if you pass a null ref, you will find out immediately that the calling code is using the type incorrectly. If there was no null reference check, the error can be obscured in many different ways.
在上面的代码中,如果你传递一个空引用,你会立即发现调用代码使用的类型不正确。如果没有空引用检查,则可以通过多种不同方式掩盖错误。
You'll notice that the .NET framework libraries nearly always fail early and often if you provide null references where it's invalid to do so. Since the exception thrown explicitly says "you messed up!" and tells you why, it makes detecting and correcting defective code a trivial task.
您会注意到 .NET 框架库几乎总是过早失败,而且如果您在无效的情况下提供空引用,则经常会失败。由于抛出的异常明确表示“你搞砸了!” 并告诉您原因,它使检测和纠正有缺陷的代码成为一项微不足道的任务。
I've heard complaints from some developers who say this practice is overly verbose and redundant as a NullReferenceException is all you need, but in practice I find it makes a big difference. This is especially the case if the call stack is deep and/or the parameter is stored and its use is deferred until later (perhaps on a different thread or obscured in some other way).
我听过一些开发人员的抱怨,他们说这种做法过于冗长和多余,因为 NullReferenceException 就是你所需要的,但在实践中我发现它有很大的不同。如果调用堆栈很深和/或参数被存储并且它的使用被推迟到以后(可能在不同的线程上或以其他方式隐藏),则尤其如此。
What would you rather have, an ArgumentNullException at the entry method, or an obscure error in the guts of it? The further you move away from the source of an error, the harder it is to trace it.
您更愿意拥有什么,入口方法中的 ArgumentNullException,或者它内部的一个不起眼的错误?离错误源越远,追踪它就越困难。
回答by Jim L
Good code analysis tools can help here. Good unit tests can also help if you're using tools that consider null as a possible path through your code. Try throwing that switch in your build settings that says "treat warnings as errors" and see if you can keep the # of warnings in your project = 0. You may find the warnings are telling you a lot.
好的代码分析工具可以在这里提供帮助。如果您使用的工具将 null 视为通过代码的可能路径,那么好的单元测试也会有所帮助。尝试在您的构建设置中加入那个“将警告视为错误”的开关,看看您是否可以将项目中的警告数量保持为 0。您可能会发现警告告诉了您很多。
One thing to keep in mind is that it may be a goodthing that you are throwing a null - reference exception. Why? because it may mean that code that shouldhave executed did not. Initializing to default values is a good idea, but you should be careful that you don't end up hiding a problem.
有一点要牢记的是,它可能是一个很好的事情,你扔空-引用异常。为什么?因为这可能意味着应该执行的代码没有执行。初始化为默认值是一个好主意,但您应该小心不要最终隐藏问题。
List<Client> GetAllClients()
{
List<Client> returnList = new List<Client>;
/* insert code to go to data base and get some data reader named rdr */
for (rdr.Read()
{
/* code to build Client objects and add to list */
}
return returnList;
}
Alright, so this may look ok, but depending on your business rules, this may be a problem. Sure, you'll never throw a null reference, but maybe your User table should never be empty? Do you want your app to be spinning in place, generating support calls from users saying "it's just a blank screen", or do you want to raise an exception that might get logged somewhere and raise an alert quickly? Don't forget to validate what you're doing as well as 'handling' exceptions. This is one of the reasons why some are loathe to take nulls out of our languages... it makes it easier to find the bugs even though it may cause some new ones.
好的,所以这看起来没问题,但根据您的业务规则,这可能是一个问题。当然,您永远不会抛出空引用,但也许您的 User 表永远不应该为空?您是否希望您的应用程序就地旋转,从用户那里获得支持电话,说“它只是一个空白屏幕”,或者您是否希望引发可能记录在某处并迅速引发警报的异常?不要忘记验证您在做什么以及“处理”异常。这就是为什么有些人不愿意从我们的语言中删除空值的原因之一......即使它可能会导致一些新的错误,也可以更容易地找到错误。
Remember: Handle exceptions, don't hide them.
记住:处理异常,不要隐藏它们。