我现在和微软代码合同工作了一段时间,今天我偶然发现了一个尴尬的案例。我的问题是 - 有一种优雅的方法来解决这种情况吗?
让我们假设我有一个简单的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#中不允许多继承。这使我处于一种情况,我必须复制合同的代码。想一想 - 在每种可能的情况下,这似乎都是错误的。
我该怎么办?
答案 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.
}
}
}