测试先决条件的不同方法的优缺点?

发布于 2024-10-07 19:27:24 字数 705 浏览 7 评论 0原文

我立即想到了 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

手长情犹 2024-10-14 19:27:24

我的猜测是,有一个合同应用于接口 IHttpHandler .ProcessRequest 要求上下文!= null。接口契约由其实现者继承,因此您无需重复 Requires。事实上,您不允许添加额外的 Requires 语句,因为您仅限于与接口契约关联的需求。

我认为区分指定合同义务和简单地执行空检查很重要。您可以实现空检查并在运行时引发异常,以此通知开发人员他们正在正确使用您的 API。另一方面,合约表达式实际上是元数据的一种形式,它可以由合约重写器解释(以引入之前手动实现的运行时异常),也可以由静态分析器解释,静态分析器可以使用它们进行推理关于您的应用程序的静态正确性。

也就是说,如果您在积极使用代码契约和静态分析的环境中工作,那么最好将断言以契约形式放置,以利用静态分析。即使您不使用静态分析,您仍然可以通过使用合约为以后的好处敞开大门。需要注意的主要事情是您是否已将项目配置为执行重写,否则合约将不会像您预期的那样导致运行时异常。


为了详细说明评论者所说的内容,Assert、Assume 和 Requires 之间的区别是:

  • Contract.Assert 表达式由合约重写器转换为断言,静态分析器尝试根据现有证据来证明该表达式。如果无法证明,您将收到静态分析警告。
  • Contract.Assume 表达式会被合约重写器忽略(据我所知),但会被静态分析器解释为可以在静态分析中考虑的新证据。 Contract.Assume 用于“填补静态分析中的空白”,无论是在缺乏复杂性来进行必要的推理的情况下,还是在与尚未用 Contract 修饰的代码进行互操作时,以便您可以假设,例如,特定函数调用返回非空结果。
  • Contract.Requires 是调用方法时必须始终为 true 的条件。它们可以是对方法参数的约束(这是最典型的),也可以是对对象的公共可见状态的约束(例如,如果 Initialized 为 True,则可能只允许调用该方法。)约束促使类的用户在使用对象时检查初始化(如果不是,则可能适当地处理错误)或创建自己的约束和/或类不变量以澄清初始化确实已经发生。

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:

  • A Contract.Assert expression is transformed into an assertion by the contract rewriter and the static analyzer attempts to prove the expression based on its existing evidence. If it can't be proven, you'll get a static analysis warning.
  • A Contract.Assume expression is ignored by the contract rewriter (as far as I know), but is interpreted by the static analyzer as a new piece of evidence it can take into account in its static analysis. Contract.Assume is used to 'fill the gaps' in the static analysis, either where it lacks the sophistication to make the necessary inferences or when inter-operating with code that has not been decorated with Contracts, so that you can Assume, for instance, that a particular function call returns a non-null result.
  • Contract.Requires are conditions that must always be true when your method is called. They can be constraints on parameters to the method (which are the most typical) and they may also be constraints on publicly visible states of the object (For instance, you might only allow the method to be called if Initialized is True.) These kinds of constraints push the users of your class to either check Initialized when using the object (and presumably handle the error appropriately if it's not) or create their own constraints and/or class invariants to clarify that Initialization has, indeed, happened.
梦在深巷 2024-10-14 19:27:24

第一种方法适合测试不应该存在的 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文