c# – 如何避免Code Contracts语句中的代码重复
我开始使用C#Code Contracts启用静态和运行时检查.问题是某些代码合同检查可能在方法之间重复,我认为没有好办法避免这种情况.
我希望完全避免静态分析仪警告,如果可能的话不要压制它. 让我们考虑一下这个例子: 有以下简单的类.这是业务逻辑模型类的常见示例: 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类中删除不必要的检查,但实际上这个不变量是无效的(例如在默认构造函数中).并且静态分析器正确检测到此违规. 我做错了什么,有一种更方便的方法来使用静态分析器的代码合同? 解决方法
您可以使用
popsicle immutability的想法并执行可能称为冰棒有效性的操作:换句话说,尽管对象在任何时候都无效,但是一旦设置了所有属性,它就会变为有效并保持这种状态.
这样,你可以在包含数据的对象中进行有效性检查,并简化使用这些对象的代码上的契约简单的事情!= 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); } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |