在一个属性中使用代码契约来提供有关另一个属性的提示

时间:2014-06-27 15:45:55

标签: c# .net visual-studio-2012 resharper code-contracts

我有struct这样的(为了简洁而简化):

public struct Period
{
    public DateTime? Start { get; private set; }
    public DateTime? End { get; private set; }

    public bool IsMoment
    {
        get { return this.Start.HasValue && this.Start == this.End; }
    }

    public Period(DateTime? start, DateTime? end) : this()
    {
        this.Start = start;
        this.End = end;
    }

    public override string ToString()
    {
        return this.IsMoment 
            ? this.Start.Value.ToString("g") 
            : string.Format("{0:g} – {1:g}", this.Start, this.End);
    }
}

一切正常,但ReSharper在this.Start.Value.ToString上显示警告:

  

可能的'System.InvalidOperationException'

如果我将IsMoment属性的主体复制到条件中,则警告消失,但我希望能够重用该属性。我可以通过评论(这是我目前所做的)禁用ReSharper警告,或者将ToString更改为string.Format,但我的代码和其他几个地方都是这样的它让我思考。我尝试使用code contracts来解决这个问题,但不幸的是我没有很多代码合同的经验,而且我不确定它的外观。

无论如何我使用代码合同向ReSharper表明如果IsMomenttrue,那么Start不是null

3 个答案:

答案 0 :(得分:1)

在IsMoment中执行类似的操作:

contract.ensures(result == false || start<> null);

(那不完全正确。我在手机上输入此内容。)

更新:

问题可能是由于多线程代码可能会更改IsMoment测试和Start.Value评估之间的Start值。

将值复制到无法被其他线程修改的局部变量可能更为正确。

public override string ToString()
{
    Period local = this;
    return local.IsMoment 
        ? local.Start.Value.ToString("g") 
        : string.Format("{0:g} – {1:g}", local.Start, local.End);
}

这看起来像是一些不必要的工作,它可能看起来效率低下,但它更多"正确"。但是,如果您的结构很小,那么在许多情况下这实际上可能更有效。

答案 1 :(得分:0)

试过在它周围包裹parens?

public override string ToString()
{
    return (this.IsMoment 
        ? this.Start.Value.ToString("g") 
        : string.Format("{0:g} – {1:g}", this.Start, this.End));
}

答案 2 :(得分:0)

这可能是使用Contract.Assume

的情况
public override string ToString()
{
   if (this.IsMoment) {
      Contract.Assume(this.Start.HasValue);
      return this.Start.Value.ToString("g");
   }
   else
      return string.Format("{0:g} – {1:g}", this.Start, this.End));
}