【问题标题】:Is there a way to write a code contract that ensures that only one method can modify a specific field?有没有办法编写代码契约来确保只有一种方法可以修改特定字段?
【发布时间】:2013-07-01 15:36:38
【问题描述】:

我有一个包含许多方法的类和一个名为 _isLoaded 的私有只读 bool 字段,它的对应属性为:public bool IsLoaded:

class MyClass
{
    readonly bool _isLoaded;
    public bool IsLoaded
    {
        get { return _isLoaded; }
    }

    public void Method1()
    {
        //does whatever
    }

    public void Method2()
    {
        //does another thing
    }

    public void Load()
    {
        //does a lot of things and then...
        _isLoaded = true;
    }
}

我知道有一些对象不变方法可以确保在调用任何公共方法后,某些对象仍保持一致状态。就像,如果我把这个添加到我的班级:

[ContractInvariantMethod]
void checkState()
{
    Contract.Invariant(!_isLoaded);
}

现在,我的问题是:有没有办法告诉运行时不要只为一个特定的公共方法(在我的例子中为 Load)调用带有 ContractInvariantMethod 注释的方法,所以我可以确定只有那个方法会正在改变我的领域的状态?

(或其他达到相同目的的方法)

谢谢。

编辑:
@Liel
非常感谢您的回答!
当我们只需要担心一个领域时,这种模式非常有效。我还添加了这个合同后置条件:

public abstract class BaseMyClass
{
    private bool _isLoaded;
    public bool IsLoaded
    {
        get { return _isLoaded; }
    }
    public virtual void Load()
    {
        Contract.Ensures(IsLoaded);
        _isLoaded = true;
    }
}

public class MyClass : BaseMyClass
{
    public override void Load()
    {
        //does a lot of things and then...
        base.Load();
    }
}

不幸的是,静态检查器不够聪明,无法确定我必须调用 base.Load() 来满足后置条件。这表明我可以 Contract.Assume(IsLoaded)... 显然,静态检查器远非完美。

【问题讨论】:

  • 如果我的回答对您有帮助,您可以随时接受它作为回答,或者至少投赞成票以表示感谢。 :)

标签: c# code-contracts


【解决方案1】:

如果我理解您想要实现的目标,您可以使用 Contract.OldValue() 构造:

public class MyClass
{
    bool _isLoaded;

    public bool IsLoaded
    {
        get { return _isLoaded; }
    }

    public void Method1()
    {
        Contract.Ensures(this._isLoaded == Contract.OldValue(this._isLoaded));
        //does whatever
        _isLoaded = false;
    }

    public void Method2()
    {
        Contract.Ensures(this._isLoaded == Contract.OldValue(this._isLoaded));
        //does another thing
    }

    public void Load()
    {
        //does a lot of things and then...
        _isLoaded = true;
    }
}

当你编译它时,你会得到以下警告:

CodeContracts: ensures unproven: this._isLoaded == Contract.OldValue(this._isLoaded)

【讨论】:

  • 我认为Contract.OldValue 是用于参数而不是字段。但如果这真的是一个好的设计,我想我会去的。因为它是一个更简单的解决方案,不需要基类来实现,而且它可以处理任意数量的字段。谁能说这是否真的是一个不错的选择?
  • 我认为将它与字段或属性一起使用是完全可以的;例如,请参阅第 15 页上的“合同缩写方法”。规则只是声明“......旧表达式必须引用存在于方法前状态中的值,即,它必须是可以只要方法的前提条件成立,就进行评估。"
  • 那应该是用户手册的第 15 页,对不起 :)
【解决方案2】:

您可以尝试以下方法:

public class BaseMyClass
{
    private bool _isLoaded;
    public bool IsLoaded
    {
        get { return _isLoaded; }
    }
    public virtual void Load()
    {
        _isLoaded = true;
    }
}

public class MyClass : BaseMyClass
{
    public void Method1()
    {
        //does whatever
    }

    public override void Load()
    {
        //does a lot of things and then...
        base.Load();
    }
}

这样您就可以从MyClass 访问IsLoaded,并确保只有Load 覆盖会更改_isLoaded 属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 1970-01-01
    • 2016-10-26
    相关资源
    最近更新 更多