代码之家  ›  专栏  ›  技术社区  ›  Arseni Mourzenko

为什么使用默认值(类型)时合同格式不正确?

  •  9
  • Arseni Mourzenko  · 技术社区  · 14 年前

    在编译使用代码契约的代码时,我有一个非常奇怪的错误,我不理解。

    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(
            this.isSubsidiary ||
            this.parentCompanyId == default(Guid));
    }
    

    失败,错误如下:

    合同格式错误。在方法“<projectname>.objectinvariant”中的赋值后发现不变量。

    如果代码修改如下:

    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(
            this.isSubsidiary ||
            this.parentCompanyId == Guid.Empty);
            // Noticed the Guid.Empty instead of default(Guid)?
    }
    

    它编译得很好。

    我的怎么了 default(Guid) ?

    1 回复  |  直到 14 年前
        1
  •  6
  •   porges    14 年前

    为此生成的IL:

    Console.WriteLine("{0}, {1}", default(Guid), Guid.Empty);
    

    是:

        .locals init (
            [0] valuetype [mscorlib]System.Guid CS$0$0000)
        L_0000: nop 
        L_0001: ldstr "{0}, {1}"
        L_0006: ldloca.s CS$0$0000
        L_0008: initobj [mscorlib]System.Guid
        L_000e: ldloc.0 
        L_000f: box [mscorlib]System.Guid
        L_0014: ldsfld valuetype [mscorlib]System.Guid [mscorlib]System.Guid::Empty
        L_0019: box [mscorlib]System.Guid
        L_001e: call void [mscorlib]System.Console::WriteLine(string, object, object)
    

    它相当于:

    Guid CS$0$0000 = new Guid();
    Console.WriteLine("{0}, {1}", CS$0$0000, Guid.Empty);
    

    代码契约直接在IL上工作,因此它认为您编写了类似于第二个版本的东西。重写器表示不允许在协定之前为变量赋值,因此它给出了一个错误。

    然而,这很奇怪,因为尽管这不起作用:

    var x = new Guid();
    Contract.Invariant(
        this.isSubsidiary ||
        this.parentCompanyId == x);
    

    确实如此,但它显然是一个“不变量前的赋值”!

    var x = Guid.Empty;
    Contract.Invariant(
        this.isSubsidiary ||
        this.parentCompanyId == x);
    

    我认为他们实际上修改了检查程序以允许这样的分配(为了使用方便),但他们不允许所有的情况…我不知道这是不是有意的。

    我会在 Code Contracts forum ,可能是一个错误。