组合接口的代码合约

时间:2014-12-01 13:01:55

标签: c# oop inheritance interface code-contracts

我现在和微软代码合同工作了一段时间,今天我偶然发现了一个尴尬的案例。我的问题是 - 有一种优雅的方法来解决这种情况吗?

让我们假设我有一个简单的trait接口,用于存储库,如下所示:

[ContractClass(typeof(CanAddContract)]
public interface ICanAdd {
    void Add(object entity);
}

属性中表示的合同如下所示:

[ContractClassFor(typeof(ICanAdd))]
internal abstract class CanAddContract {

    public void Add(object entity) {
        Contract.Requires(object != null); // guard against null argument
    }

}

所以,现在我们已经获得了类似的实体删除特征

[ContractClass(typeof(CanDeleteContract))]
public interface ICanDelete {
    void Delete(object entity);
}

......和合同......

[ContractClassFor(typeof(ICanDelete))]
internal abstract class CanDeleteContract {
    public void Delete(object entity) {
        Contract.Requires(entity != null); // guard against null argument
    }
}

没错。但由于接口表示存储库特征,因此它们用于组成存储库接口:

public interface IEntityStore : ICanAdd, ICanDelete {

    void SomeOtherMethodThatNeedsAContract();

}

现在怎样?当我想为这个接口创建一个契约类时,我必须重新实现上面描述的两个契约类,因为在C#中不允许多继承。这使我处于一种情况,我必须复制合同的代码。想一想 - 在每种可能的情况下,这似乎都是错误的。

我该怎么办?

1 个答案:

答案 0 :(得分:4)

CodeContracts编译时重写器将自动发现并使用所有基接口的合同。

对于您的具体示例(请注意您不需要重复任何基本接口'合同,但它们仍然有效):

using System;
using System.Diagnostics.Contracts;

namespace Demo
{
    [ContractClass(typeof(CanAddContract))]
    public interface ICanAdd
    {
        void Add(object entity);
    }

    [ContractClassFor(typeof (ICanAdd))]
    internal abstract class CanAddContract: ICanAdd
    {
        public void Add(object entity)
        {
            Contract.Requires(entity != null);
        }
    }

    [ContractClass(typeof(CanDeleteContract))]
    public interface ICanDelete
    {
        void Delete(object entity);
    }

    [ContractClassFor(typeof(ICanDelete))]
    internal abstract class CanDeleteContract: ICanDelete
    {
        public void Delete(object entity)
        {
            Contract.Requires(entity != null);
        }
    }

    [ContractClass(typeof(EntityStoreContract))]
    public interface IEntityStore: ICanAdd, ICanDelete
    {
        void SomeOtherMethodThatNeedsAContract(object entity);
    }

    // Note how we only specify the additional contract for SomeOtherMethodThatNeedsAContract().
    // We do NOT need to repeat the contracts for ICanAdd and ICanDelete.
    // These contracts are automatically inferred from the ICanAdd and ICanDelete contracts.

    [ContractClassFor(typeof(IEntityStore))]
    internal abstract class EntityStoreContract: IEntityStore
    {
        public void SomeOtherMethodThatNeedsAContract(object entity)
        {
            Contract.Requires(entity != null);
        }

        public abstract void Add(object entity);
        public abstract void Delete(object entity);
    }

    public sealed class EntityStore: IEntityStore
    {
        public void Add(object entity)
        {
        }

        public void Delete(object entity)
        {
        }

        public void SomeOtherMethodThatNeedsAContract(object entity)
        {
        }
    }

    public static class Program
    {
        private static void Main()
        {
            var entityStore = new EntityStore();

            entityStore.Add(null); // This will correctly give a code contracts exception.
        }
    }
}
相关问题