私有静态只读字段上的代码约定和失败

时间:2012-06-19 12:08:40

标签: c# settings code-contracts contracts requires

我班上有一个私有静态只读字段:

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 个答案:

答案 0 :(得分:1)

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

  1. 您可以将设置添加到项目设置中,以输出要应用的正确属性,以忽略某些警告。这是通过将“-outputwarnmasks”标志添加到项目文件设置的“代码合同”选项卡中“高级”部分下的“额外静态检查器选项”来完成的。这将向“构建输出”窗口添加信息,为您添加正确的属性以忽略各个案例。 (在处理实体框架时非常有用)。
  2. 您可以重写代码,为代码添加正确的需求和保证,以便不显示警告。
  3. 如果要重写代码: 要解决问题#1,您必须包装Settings类并将新的MyDirectoryPath公开为非生成代码的属性,以便您可以在其中添加一个检查并返回一个空字符串并添加Contract.Ensures(Contract.Result<string>() != null) at该物业的吸气之巅。

    要解决问题#2,您必须在私有静态属性中包含对类字段的访问权限,以添加适当的保证和需求。

    我通常会尽可能重写代码,除了使用Entity Framework / LINQ,您需要添加属性,尤其是复杂查询。

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

答案 1 :(得分:1)

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

解决此问题的方法是将设置包装在具有合同的方法中。 E.g:

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的预期行为,但运行时检查仍将到位以确保您的信念正确(前提是您将检查保留在代码中)。< / p>

var path = MyDirectory.FullName;
Contract.Assume(!string.IsNullOrEmpty(path));
var catalog = new DirectoryCatalog(path);

答案 2 :(得分:0)

那么,对于问题#2,我认为您可能希望使用&&而不是||。但除此之外,也许对于问题#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);
}

我没有太多使用Contract API的经验,所以我可能会因此而脱离摇滚乐。 :)

答案 3 :(得分:0)

  

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

不要这样做! MyDirectory.Exists可以随时更改 ,并且来电者无法保证。如果目录不存在,则抛出异常 - 这就是例外情况。