协变接口与逆变接口作为成员 属性

Covariant interface with contravariant interface as member property

我有一个接口,它定义了一个 reader 和任何 IFoo 的编写器。

public interface IFoobarStore<out E>
 where E : class, IFoobar
{
    IFoobarReader<E> GetReader();
    IFoobarWriter<E> GetWriter();
}

IFoobarStore 是协变的。 IFoobarStore 与任何派生的 IFoo 交互。因此,任何更派生的 IFoo 都应该可以分配给更派生的 IFoo 类型参数。

// DerivedFoobityStore.cs
public sealed class DerivedFoobityStore
 : IFoobarStore<MyFoobity>
{
    // implementation follows
}

如果 IFoobarStore 被定义为带有 IFoobarStore<E> 而不是 IFoobarStore<out E> 的变体,以下将产生编译器错误 CS0266。

IFoobarStore<IFoo> myGenericStore = new DerivedFoobityStore();

reader 也被定义为协变的。它应该允许从某处读取派生的 IFoo 对象。

using System.Collections.Generic;
public interface IFoobarReader<out E>
 where E : class, IFoo
{
    IEnumerable<E> GetAll();
    IEnumerable<E> GetBy(params object[] vars);
    E GetSingle(object uniqueIdentifier);
}

IFoobarWriter 公开用于任何 IFoo 上的标准 CRUD 操作的成员。

public interface IFoobarWriter<in E>
 where E : class, IFoo
{
    void Add(E foo);
    int Delete(E foo);
    E Update(E foo);
}

由于每个操作都有一个类型为 E 的参数(任何 class 派生自 IFoo),IFoobarWriter 必须被标记为逆变。

当我编译代码时收到此错误:

Invalid variance: The type parameter 'E' must be contravariantly valid on 'IFoobarStore<E>.GetWriter()'. 'E' is covariant.

如何更好地重构此代码以使其编译成功?

目前我通过重构 IFoobarWriter 来使用对象而不是 IFoo 来解决这个问题。

public interface IFoobarWriter<out E>
 where E : class, IFoo
{
    void Add(object foo);
    int Delete(object foo);
    object Update(object foo);
}

这使得 IFoobarWriter 的基本前提过时了。

解决方案是删除 E 作为 IFoobarWriter 实例成员方法的可接受参数。

public interface IFoobarWriter<out E>
 where E : class, IFoo
{
    void Add(IFoo foo);
    int Delete(IFoo foo);
    object Update(IFoo foo);
}

通过让添加、删除和更新接受 IFoo,它们有效地限制了它们可以处理的类型(而不是将参数设置为 object),足以满足某些业务需求。

让 IFoobarWriter 的类型参数 E 保持协变允许它保留为 IFoobarStore 接口的一部分。