【问题标题】:Code Contracts: How to deal with inherited interfaces?代码契约:如何处理继承的接口?
【发布时间】:2026-01-21 05:20:03
【问题描述】:

我正在使用 MS Code Contracts,但在使用接口继承和 ContractClassFor 属性时遇到了障碍。

鉴于这些接口和契约类:

[ContractClass(typeof(IOneContract))]
interface IOne { }
[ContractClass(typeof(ITwoContract))]
interface ITwo : IOne { }

[ContractClassFor(typeof(IOne))]
abstract class IOneContract : IOne { }
[ContractClassFor(typeof(ITwo))]
abstract class ITwoContract : IOneContract, ITwo { }

假设 IOne 和 ITwo 是实体接口。因此 IOneContract 将包含大量代码以进行必要的检查。

我不想在 IOne 接口的 ITwoContract 中复制所有这些内容。我只想为 Itwo 接口添加新合同。从另一个合同类继承一个合同类似乎是重用该代码的可能方式。但是我收到以下错误:

EXEC : warning CC1066: Class 'ITwoContract' is annotated as being the contract for the interface 'ITwo' and cannot have an explicit base class other than System.Object.

这是代码合同的限制还是我做错了?我们的项目中有很多接口继承,如果我不知道如何解决这个问题,这感觉就像是代码合同的交易破坏者。

【问题讨论】:

    标签: code-contracts


    【解决方案1】:

    代替:

    [ContractClassFor(typeof(ITwo))]
    abstract class ITwoContract : IOneContract, ITwo { }
    

    只需继承合约:

    [ContractClassFor(typeof(ITwo))]
    abstract class ITwoContract : ITwo { }
    

    您只需提供ITwo 中新方法的合同。来自IOneContract 的合约将被自动继承,您可以将所有继承的IOne 方法声明为抽象——事实上,您不能ITwoContract 上为IOne 提供合约,或者CC会抱怨:)

    例如,如果你有这个:

    [ContractClass(typeof (IOneContract))]
    interface IOne
    {
        int Thing { get; }
    }
    
    [ContractClass(typeof (ITwoContract))]
    interface ITwo : IOne
    {
        int Thing2 { get; }
    }
    
    [ContractClassFor(typeof (IOne))]
    abstract class IOneContract : IOne
    {
        public int Thing
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() > 0);
                return 0;
            }
        }
    }
    
    [ContractClassFor(typeof (ITwo))]
    abstract class ITwoContract : ITwo
    {
        public int Thing2
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() > 0);
                return 0;
            }
        }
    
        public abstract int Thing { get; }
    }
    

    然后这个实现将在这两种方法上都说“未经证实的合同”,正如预期的那样:

    class Two : ITwo
    {
        public int Thing
        {
            get { return 0; }
        }
    
        public int Thing2
        {
            get { return 0; }
        }
    }
    

    【讨论】:

    • 啊! “并且您可以将所有继承的 IOne 方法声明为抽象”位是我缺少的关键部分。谢谢。
    • 问题是,你仍然需要在抽象合约类ITwoContract中指定所有的IOne方法。如果 IOne 有 50 种方法,我需要为从 IOne 继承的每个接口重复它们 - 非常乏味,并且很难对 IOne 进行更改。
    • 如果您有一个包含 50 个方法的接口,那么您可能会发现与该接口相关的每一个更改都很乏味和困难,因为您将违反单一职责原则。
    • @porges 你知道标有ContractClassForAttribute 的类是否可以包含Contract.Assume(...) 合约?我的意思是,我尝试将Contract.Assume(...) 放在密封的具体合约类中(实现一个抽象类),但假设似乎并没有“回流”到指定合约的类。我不断收到带有建议声明的CodeContracts: Suggested assume: ...(我已经将其放入标有ContractClassForAttribute 的合同类中)。
    • @porges 没关系。我错误地推断了Contract.Assume 的意图。这用于您的实际实现代码中,以告诉静态检查器您认为在代码中此时陈述的条件应该为真,因此它本身不是合同。只是对静态检查器的提示。