8

我创建了一些类库,其中一些被世界各地的其他人使用,现在我开始使用 Visual Studio 2010 我想知道切换到使用代码合同而不是常规的旧 - 对我来说是多么好的主意风格的 if 语句。

IE。而不是这个:

if (fileName == null)
    throw new ArgumentNullException("fileName");

用这个:

Contract.Requires(fileName != null);

我问的原因是我知道静态检查器对我不可用,所以我对我做出的一些假设有点紧张,编译器无法验证。这可能会导致类库不为下载它的人编译,当他们有静态检查器时。再加上我什至无法重现该问题,修复起来会很烦人,而且我认为,如果它看起来甚至没有从盒子。

所以我有几个问题:

  • 如果您有权访问静态检查器,它是否默认打开?或者是否有我需要在类库中打开的设置(因为我没有静态检查器,所以我不会)
  • 我的担心是没有根据的吗?上述情况是一个真正的问题吗?

任何的建议都受欢迎。


编辑:让我澄清一下我的意思。

假设我在一个类中有以下方法:

public void LogToFile(string fileName, string message)
{
    Contracts.Requires(fileName != null);
    // log to the file here
}

然后我有这个代码:

public void Log(string message)
{
    var targetProvider = IoC.Resolve<IFileLogTargetProvider>();
    var fileName = targetProvider.GetTargetFileName();
    LogToFile(fileName, message);
}

现在,在这里,IoC 开始了,解决了一些“随机”类,为我提供了一个文件名。假设对于这个库,我不可能取回一个不会给我一个非空文件名的类,但是,由于 IoC 调用的性质,静态分析无法验证这一点,因此可能假设一个可能的值可能为空。

LogToFile因此,静态分析可能会得出结论,即存在使用参数调用方法的风险null,因此无法构建。

我知道我可以在代码中添加假设,说编译器应该接受它,因为fileName我从那个方法得到的永远不会为空,但是如果我没有静态分析器(VS2010 Professional),上面代码会为我编译,因此我可能会将它作为一个沉睡的错误留给拥有 Ultimate 的人来查找。换句话说,不会有编译时警告说明这里可能有问题,所以我可能会按原样发布库。

那么这是一个真实的场景和问题吗?

4

3 回答 3

2

首先,静态检查器实际上(据我所知)仅在最终/学术版本中可用- 因此,除非您组织中的每个人都使用它,否则如果他们可能违反不变量,则可能不会收到警告。

其次,虽然静态分析令人印象深刻,但它并不总能找到可能导致违反不变量的所有路径。然而,这里的好消息是Requires 合约在运行时保留- 它在 IL 转换步骤中处理 - 因此检查在编译时运行时都存在。通过这种方式,它等同于(但优于)常规if()检查。

您可以在此处阅读有关代码合约编译执行的运行时重写的更多信息,也可以在此处阅读详细手册

编辑:根据我从手册中收集到的信息,我怀疑您描述的情况确实是可能的。但是,我认为这些将是警告而不是编译错误 - 您可以使用System.Diagnostics.CodeAnalysis.SuppressMessage(). 拥有静态验证器的代码使用者也可以标记要忽略的特定案例 - 但如果有很多案例,这肯定会带来不便。我将尝试在今天晚些时候找到一些时间来对您的场景进行最终测试(我目前无法访问静态验证器)。

这里有一个非常棒的博客,它几乎完全致力于代码合同(如果你还没有看过的话)可能有一些你感兴趣的内容。

于 2010-04-13T18:09:06.997 回答
2

当你的LogToFileLog方法都是你的库的一部分时Log,一旦你打开静态检查器,你的方法可能不会编译。当然,当您向使用静态检查器编译您的代码的其他人提供代码时,也会发生这种情况。但是,据我所知,您客户的静态检查器不会验证您交付的程序集的内部结构。它会根据程序集的公共 API 静态检查自己的代码。因此,只要您只是发布 DLL,就可以了。

当然,对于实际启用了静态检查器的用户来说,提供一个非常烦人的 API 的库是有变化的,所以我认为如果你测试了 API 的可用性,建议只提供带有合同定义的库无论有无静态检查器。

请注意将现有if (cond) throw ex调用更改Contracts.Requires(cond)为对您已在先前版本中提供的公共 API 调用的调用。请注意,该Requires方法抛出的异常(RequiresViolationException如果我没记错的话是 a )与您通常抛出的异常 (a ArgumentException) 不同。在这种情况下,请使用Contract.Requires重载。这样,您的 API 接口将保持不变。

于 2010-04-13T18:44:53.427 回答
0

不; 静态分析器永远不会阻止编译成功(除非它崩溃!)。

静态分析器会警告您未经证实的前置/后置条件,但不会停止编译。

于 2010-04-14T00:03:22.557 回答