【问题标题】:Code Contracts for composed interfaces组合接口的代码协定
【发布时间】:2014-12-01 13:01:55
【问题描述】:

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

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

[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# 中不允许多重继承。这让我不得不为合同复制代码。想一想 - 在所有可能的情况下,这对我来说都是错误的。

我能做些什么呢?

【问题讨论】:

  • @MatthewWatson 这对我没有帮助,因为 ICanDelete(假设您链接的问题中的 ITwo)没有实现 ICanAdd (IOne)。它们是独立的、不相关的接口,而只有 IEntityStore 实现了两者(以及一些其他特定于 IEntityStore 的方法)。请仔细阅读问题。
  • 当然对你有帮助。我正在添加一个答案,它准确地显示了你如何做到这一点。
  • @MatthewWatson 似乎我过于关注链接中提供的示例,而不是其背后的总体思路。您提供的答案完美无缺,所以谢谢! (也将其发布为答案。这样我可以给你一些想象的互联网点。)

标签: c# oop inheritance interface code-contracts


【解决方案1】:

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.
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多