【发布时间】:2013-10-26 06:07:47
【问题描述】:
我开始使用启用了静态和运行时检查的 C# 代码协定。问题是一些代码契约检查可能会在方法之间重复,我认为没有好的方法可以避免这种情况。
我希望完全避免静态分析器警告,并且尽可能不要抑制它。
让我们考虑这个例子:
有以下简单的类。这是业务逻辑模型类的常见示例:
class Category
{
public string Name { get; set; }
}
class Article
{
public string Title { get; set; }
public string Content { get; set; }
public Category Category { get; set; }
}
对于一些基于反射的技术(如 MVC 中的模型绑定、数据库映射),我们需要为模型属性提供公共默认构造函数和公共设置器。所以我们不能保证,例如对于 Category,Contract.Invariant(!string.IsNullOrEmpty(Name)) 总是正确的。
然后我们在内部 CategoryRepository 类中创建下一个方法。我们假设所有验证都较早通过并且只接受有效类别:
public void Add(Category category)
{
Contract.Requires(category != null);
Contract.Requires(!string.IsNullOrEmpty(category.Name));
...
}
到目前为止一切顺利。然后我们在 ArticleRepository 中添加类似的方法:
public void Add(Article article)
{
Contract.Requires(article != null);
Contract.Requires(!string.IsNullOrEmpty(article.Title));
Contract.Requires(!string.IsNullOrEmpty(article.Content));
Contract.Requires(article.Category != null);
Contract.Requires(!string.IsNullOrEmpty(article.Category.Name));
...
}
问题是:
1) 在我们期望通过合同获得有效类别的每个地方,我们都需要重复检查,例如:
Contract.Requires(category != null);
Contract.Requires(!string.IsNullOrEmpty(category.Name));
有时我们还需要在 Contract.Assume 方法中进行这些检查。
2) 外部类(如 Article)应检查 Category 类的合同。看起来违反了LoW 和基本封装原则。
我尝试了下一个解决方案:
1) 将重复代码提取到 Category 类中的纯方法中,如下所示:
[Pure]
public static bool Valid(Category category)
{
if (category == null)
return false;
return !string.IsNullOrEmpty(category.Name);
}
这样的使用合同:
Contract.Requires(Category.Valid(category));
不是很好的解决方案,也不起作用 - 静态分析器不满意。
2) 为 Category 定义一个不变量:
[ContractInvariantMethod]
void Invariant()
{
Contract.Invariant(!string.IsNullOrEmpty(Name));
}
这个解决方案非常好,允许从 Category 类中删除不必要的检查,但实际上这个不变量是无效的(例如在默认构造函数中)。静态分析器正确检测到这种违规行为。
我是不是做错了什么,是否有更方便的方法可以将代码合同与静态分析器结合使用?
【问题讨论】:
标签: c# c#-4.0 refactoring code-contracts