4

我的班级中有一个私有静态只读字段:

public class MyClass
{
    // ISSUE #1 -- requires unproven: path != null
    private static readonly DirectoryInfo MyDirectory =
        new DirectoryInfo(Settings.Default.MyDirectoryPath);

    protected virtual void SomeMethod()
    {
        if (MyDirectory.Exists)
        {
            // ISSUE #2 -- requires unproven: !string.IsNullOrEmpty(path)
            var catalog = new DirectoryCatalog(MyDirectory.FullName);
        }
    }
}

对于问题 #1,我使用了一个空合并运算符来默认为某个魔术字符串并修复了它,但我真的不喜欢那个解决方案。我希望有更好的解决方案。

对于问题 #2,我唯一能想到的是使用 Contract.Assumes,因为如果我尝试使用Contract.Requires(MyDirectory.Exists || !String.IsNullOrEmpty(MyDirectory.FullName));它会抱怨可见性(在受保护方法的要求中使用的私有字段)。

4

4 回答 4

1

在当前项目中使用代码合同一段时间后,我发现它确实有时会迫使您重写代码以纠正问题。你真的有两个选择。

  1. 您可以将该设置添加到您的项目设置中,以输出要应用的正确属性是忽略某些警告。这是通过将“-outputwarnmasks”标志添加到项目文件设置的“代码合同”选项卡的“高级”部分下的“额外静态检查器选项”来完成的。这会将信息添加到构建输出窗口,为您添加正确的属性以忽略个别情况。(在处理实体框架时非常有用)。
  2. 您可以重写您的代码以将正确的 Requires 和 Ensures 添加到您的代码中,这样警告就不会出现。

如果您想重写代码:要解决问题 #1,您必须包装 Settings 类并将新的 MyDirectoryPath 作为不是代码生成的属性公开,以便您可以在其中添加检查并返回一个空字符串和Contract.Ensures(Contract.Result<string>() != null)在属性的 Getter 顶部添加。

要解决问题 #2,您必须将对类字段的访问封装在一个私有静态属性中,该属性添加了正确的 Ensures 和 Requires。

我通常会尽可能地重写代码,除了需要添加属性的实体框架/LINQ,尤其是复杂查询。

** 免责声明 ** 这些只是我找到的解决问题的方法,因为没有大量关于解决这些类型项目的其他方法的信息。

于 2012-06-19T12:28:15.413 回答
1

问题 #1是由 Visual Studio 生成的代码的结果,Settings.Default.MyDirectoryPath而该属性没有任何合同。此问题不仅限于空字符串。现在许多 API 的合约要求 aTimeSpan为非负数,但直接在 API 中使用设置会生成代码合约警告。

解决此问题的一种方法是将设置包装在具有合同的方法中。例如:

String GetMyDirectoryPath() {
  Contract.Ensures(Contract.Result<String>() != null);
  var myDirectoryPath = Settings.Default.MyDirectoryPath;
  Contract.Assume(myDirectoryPath != null);
  return myDirectoryPath;
}

请注意Contract.Assume实际如何验证您的设置(代码合同无法验证,因为它由外部配置文件控制)。如果它TimeSpan是预期为非负数的,您可以使用您自己的异常来Contract.Assume进行验证,从而导致ContractException使用您自己的异常或其他方法。

添加这个额外的层有点乏味,但是因为设置是在应用程序之外定义的,所以它需要在某个时候进行运行时验证,就像您必须验证交互式用户输入一样。

问题 #2可能是因为DirectoryInfo没有定义任何合同。最简单的方法是使用Contract.Assume. 这将声明您认为是预期行为,DirectoryInfo但运行时检查仍将到位,以确保您的信念是正确的(前提是您将检查保留在代码中)。

var path = MyDirectory.FullName;
Contract.Assume(!string.IsNullOrEmpty(path));
var catalog = new DirectoryCatalog(path);
于 2012-06-19T12:55:01.090 回答
0

好吧,对于问题#2,我想你可能想使用&&not ||。但除此之外,也许对于问题#1,您可以将这些检查放在静态构造函数中?问题#2 的另一个选项是将目录作为参数的方法:

private static readonly DirectoryInfo MyDirectory;

static MyClass()
{
    Contract.Requires(Settings.Default.MyDirectoryPath != null);
    MyDirectory = new DirectoryInfo(Settings.Default.MyDirectoryPath);
}

protected void SomeMethod()
{
    SomeOtherMethod(MyDirectory);
}

protected virtual void SomeOtherMethod(DirectoryInfo directory)
{
    Contract.Requires(directory.Exists && !String.IsNullOrEmpty(directory.FullName));

    var catalog = new DirectoryCatalog(directory.FullName);
}

我没有太多使用ContractAPI 的经验,所以我可能会对这一切感到不快。:)

于 2012-06-19T12:28:36.663 回答
0

Contract.Requires(MyDirectory.Exists || !String.IsNullOrEmpty(MyDirectory.FullName));

不要这样做!MyDirectory.Exists可以随时更改调用者无法保证。如果目录不存在,只需抛出异常 - 这就是异常的用途。

于 2012-06-26T03:29:00.100 回答