加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 如何避免Code Contracts语句中的代码重复

发布时间:2020-12-15 21:57:56 所属栏目:百科 来源:网络整理
导读:我开始使用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);
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读