代码契约是否未能发现Nullable <T> .HasValue和null之间的明显关系?

Whe*_*lie 11 c# code-contracts static-code-analysis

我正在尝试将代码契约应用于我的代码,我遇到了一个令人困惑的问题.这段代码无法满足合同,但除非我真的很厚,否则我希望它能够轻松分析id在返回时必须有一个值

if (id == null)
    throw new InvalidOperationException(string.Format("{0} '{1}' does not yet have an identity", typeof(T).Name, entity));

return id.Value;
Run Code Online (Sandbox Code Playgroud)

代码合同错误:需要未经证实:HasValue

Whe*_*lie 6

我已经到了这个行为的底部,这不是Code Contract的错.

我在ILSpy中打开生成的程序集,这是生成的代码:

public Guid Id
{
    get
    {
        Guid? guid = this.id;
        if (!guid.HasValue)
        {
            throw new InvalidOperationException();
        }
        guid = this.id;
        return guid.Value;
    }
}
Run Code Online (Sandbox Code Playgroud)

id正在将实例变量复制到局部变量,并且在条件块之后将此局部变量重置回其原始值.现在很明显为什么Code Contracts显示合同违规错误,但仍然让我感到困惑,为什么代码被重写为这种形式.我做了一些实验,并将Code Contracts完全从项目中删除,很明显这是标准的C#编译器行为,但为什么呢?

这个秘密似乎是由于我在原始问题中意外遗漏的一个小细节.该id实例变量声明为readonly这似乎是负责使编译器添加的临时guid变量.

我必须承认,我仍然困惑为什么编译器认为它需要这样做以确保不变性的保证,id但我会继续挖掘......

  • 如果您阅读了C#语言规范,您会发现一些内容表明`readonly`字段的值类型不是变量.与属性一样,它们被视为值,对于值类型,它意味着必须在副本上完成对它们的所有操作.就像访问值类型的属性一样,每次访问值类型的`readonly`字段时,它都会生成一个副本.事实上,我知道这是导致我推测这是基于反编译代码的问题的原因. (4认同)