【问题标题】:Using code contracts in one property to provide hints about another property在一个属性中使用代码协定来提供关于另一个属性的提示
【发布时间】:2014-06-27 15:45:55
【问题描述】:

我有一个像这样的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

【问题讨论】:

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


    【解决方案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);
    }
    

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

    【讨论】:

    • 这个想法看起来很有希望。我尝试了Contract.Ensures(!Contract.Result<bool>() || this.Start.HasValue) 和其他几个变体。不幸的是,它们似乎都不起作用。 ReSharper 警告仍然存在。
    • 我将继续接受这个答案。它似乎使静态合同检查器高兴,但不是 ReSharper。我猜 ReSharper 的检查器不够复杂,无法检查在其他属性或方法中定义的合同(或者至少在这种情况下不是)。最后,我把它改成了this.Start.GetValueOrDefault().ToString("g"),满足了ReSharper,而且我认为绕过NullableHasValue检查甚至可能效率更高。
    【解决方案2】:

    试过用括号包裹它吗?

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

    【讨论】:

    • 我试过这个,但它并没有改变行为。我还尝试了其他几种形式,包括将?: 更改为if/else
    【解决方案3】:

    这可能是使用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));
    }
    

    【讨论】:

    • 谢谢,这确实解决了错误,但我必须将它复制到我打电话给IsMoment 的每个地方。我希望通过IsMoment 内部的合同来做到这一点。
    猜你喜欢
    • 2022-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-17
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 2010-11-14
    相关资源
    最近更新 更多