测试先决条件的不同方法的优缺点?
我立即想到了 4 种检查空参数的方法:
Debug.Assert(context != null);
Contract.Assert(context != null);
Contract.Requires(context != null);
if (context == null) throw new ArgumentNullException("context");
我一直使用最后一种方法,但我刚刚看到一个使用 Contract.Requires
的代码片段,我将其我不熟悉。 每种方法的优点/缺点是什么?还有其他方法吗?
在 VS2010 w/ Resharper 中,
Contract.Assert
警告我该表达式始终为 true(它是如何知道的,我不太确定......不能HttpContext 为空?),Contract.Requires
淡出,它告诉我编译器不会调用该方法(我假设由于前一个原因,它永远不会为空),- 如果我将最后一个方法更改为
context != null
后面的所有代码都会淡出,它告诉我该代码是启发式无法访问的。
因此,最后 3 个方法似乎在 VS 静态检查器中内置了某种智能,而 Debug.Assert 就是愚蠢的。
Off the top of my head, I can think of 4 ways to check for null arguments:
Debug.Assert(context != null);
Contract.Assert(context != null);
Contract.Requires(context != null);
if (context == null) throw new ArgumentNullException("context");
I've always used the last method, but I just saw a code snippet that used Contract.Requires
, which I'm unfamiliar with. What are the advantages/disadvantages of each method? Are there other ways?
In VS2010 w/ Resharper,
Contract.Assert
warns me that the expression is always true (how it knows, I'm not quite sure... can't HttpContext be null?),Contract.Requires
gets faded out and it tells me the compiler won't invoke the method (I assume because of the former reason, it will never be null), and- if I change the last method to
context != null
all the code following gets faded out and it tells me the code is heuristically unreachable.
So, it seems the last 3 methods have some kind of intelligence built into the VS static checker, and Debug.Assert
is just dumb.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我的猜测是,有一个合同应用于接口 IHttpHandler .ProcessRequest 要求上下文!= null。接口契约由其实现者继承,因此您无需重复 Requires。事实上,您不允许添加额外的 Requires 语句,因为您仅限于与接口契约关联的需求。
我认为区分指定合同义务和简单地执行空检查很重要。您可以实现空检查并在运行时引发异常,以此通知开发人员他们正在正确使用您的 API。另一方面,合约表达式实际上是元数据的一种形式,它可以由合约重写器解释(以引入之前手动实现的运行时异常),也可以由静态分析器解释,静态分析器可以使用它们进行推理关于您的应用程序的静态正确性。
也就是说,如果您在积极使用代码契约和静态分析的环境中工作,那么最好将断言以契约形式放置,以利用静态分析。即使您不使用静态分析,您仍然可以通过使用合约为以后的好处敞开大门。需要注意的主要事情是您是否已将项目配置为执行重写,否则合约将不会像您预期的那样导致运行时异常。
为了详细说明评论者所说的内容,Assert、Assume 和 Requires 之间的区别是:
My guess is that there is a contract applied to the interface IHttpHandler.ProcessRequest which requires that context != null. Interface contracts are inherited by their implementers, so you don't need to repeat the Requires. In fact, you are not allowed to add additional Requires statements, as you are limited to the requirements associated with the interface contract.
I think it's important to make a distinction between specifying a contractual obligation vs. simply performing a null check. You can implement a null check and throw an exception at runtime, as a way to inform the developer that they are using your API correctly. A Contract expression, on the other hand, is really a form of metadata, which can be interpreted by the contract rewriter (to introduce the runtime exceptions that were previously implemented manually), but also by the static analyzer, which can use them to reason about the static correctness of your application.
That said, if you're working in an environment where you're actively using Code Contracts and static analysis, then it's definitely preferable to put the assertions in Contract form, to take advantage of the static analysis. Even if you're not using the static analysis, you can still leave the door open for later benefits by using contracts. The main thing to watch out for is whether you've configured your projects to perform the rewriting, as otherwise the contracts will not result in runtime exceptions as you might expect.
To elaborate on what the commenters have said, the difference between Assert, Assume and Requires is:
第一种方法适合测试不应该存在的 null 条件。也就是说,在开发期间使用它以确保它不会意外设置为 null。由于它不执行任何错误处理,因此这不适合处理已发布产品中的空条件。
我想说第二个和第三个版本是相似的,因为它们不以任何方式处理这个问题。
一般来说,如果变量在最终产品中实际上可能为空,则使用最后一个版本。您可以在那里进行特殊处理,或者只是像您所做的那样引发异常。
The first method is appropriate for testing for a null condition that should never exist. That is, use it during development to ensure it doesn't unexpectedly get set to null. Since it doesn't do any error handling, this is not appropriate for handling null conditions in your released product.
I would say the 2nd and 3rd versions are similar in that they don't handle the issue in any way.
In general, if there's a possibility that the variable could actually be null in the final product, the last version is the one to use. You could do special handling there, or just raise an exception as you've done.