我希望完全避免静态分析仪警告,如果可能的话不要压制它.
让我们考虑一下这个例子:
有以下简单的类.这是业务逻辑模型类的常见示例:
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中的模型绑定,数据库映射),我们需要为模型属性提供公共默认构造函数和公共setter.所以我们不能保证例如对于Category,Contract.Invariant(!string.IsNullOrEmpty(Name))始终为true.
然后我们在内部的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)外部类(如文章)应检查类别类别的合同.看起来违反了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类中删除不必要的检查,但实际上这个不变量是无效的(例如在默认构造函数中).并且静态分析器正确检测到此违规.
解决方法
这样,你可以在包含数据的对象中进行有效性检查,并简化使用这些对象的代码上的契约简单的事情!= null&& thing.IsValid().
以下是一些演示此方法的代码.静态检查器仍然需要一些帮助来证明a是有效的,因为它的属性是独立设置的,但这可能是你想要在你的对象通过反射构造之后做的检查.
internal class Program { private static void Main() { var c = new Category(); c.Name = "Some category"; var categoryRepository = new CategoryRepository(); categoryRepository.Add(c); var a = new Article(); a.Category = c; a.Content = "Some content"; a.Title = "Some title"; var repository = new ArticleRepository(); // give the static checker a helping hand // we don't want to proceed if a is not valid anyway if (!a.IsValid) { throw new InvalidOperationException("Hard to check statically"); // alternatively,do "Contract.Assume(a.IsValid)" } repository.Add(a); Console.WriteLine("Done"); } } public class Category { private bool _isValid; public bool IsValid { get { return _isValid; } } private string _name; public string Name { get { return _name; } set { Contract.Requires(!string.IsNullOrEmpty(value)); Contract.Ensures(IsValid); _name = value; _isValid = true; } } [ContractInvariantMethod] void Invariant() { Contract.Invariant(!_isValid || !string.IsNullOrEmpty(_name)); } } public class Article { private bool _isValid; public bool IsValid { get { return _isValid; } } private string _title; public string Title { get { return _title; } set { Contract.Requires(!string.IsNullOrEmpty(value)); _title = value; CheckIsValid(); } } private string _content; public string Content { get { return _content; } set { Contract.Requires(!string.IsNullOrEmpty(value)); _content = value; CheckIsValid(); } } private Category _category; public Category Category { get { return _category; } set { Contract.Requires(value != null); Contract.Requires(value.IsValid); _category = value; CheckIsValid(); } } private void CheckIsValid() { if (!_isValid) { if (!string.IsNullOrEmpty(_title) && !string.IsNullOrEmpty(_content) && _category != null && _category.IsValid) { _isValid = true; } } } [ContractInvariantMethod] void Invariant() { Contract.Invariant( !_isValid || (!string.IsNullOrEmpty(_title) && !string.IsNullOrEmpty(_content) && _category != null && _category.IsValid)); } } public class CategoryRepository { private readonly List<Category> _categories = new List<Category>(); public void Add(Category category) { Contract.Requires(category != null); Contract.Requires(category.IsValid); Contract.Ensures(category.IsValid); _categories.Add(category); } } public class ArticleRepository { private readonly List<Article> _articles = new List<Article>(); public void Add(Article article) { Contract.Requires(article != null); Contract.Requires(article.IsValid); Contract.Ensures(article.IsValid); _articles.Add(article); } }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。