变体通用接口

时间:2016-06-06 20:00:59

标签: c#-4.0 covariance

我有一个通用接口,以及一个用具体类型参数实现该接口的类。我还有一个使用泛型接口作为其类型约束的泛型类,但type参数被限制为某个基类的子类。我想用实现该接口的类来实例泛型类,但是有一个将类转换为该接口的问题。以下代码说明了我提到的所有类:

基类:

class DomainBase
{
}

该类用作接口

中的类型参数
class Person : DomainBase
{
}

通用界面:

public interface IRepository<T> where T : class
{
    IEnumerable<T> Fetch();
    T Persist(T item);
}

实现通用接口的类:

class PersonRepository : IRepository<Person>
{
    public IEnumerable<Person> Fetch()
    {
        ...
    }
    public Person Persist(Person item)
    {
        ...
    }
}

使用通用接口的泛型类:

class DomainBaseViewModel<Repository>
    where Repository : IRepository<DomainBase>, new()
{
    private Repository repository = new Repository();
    private ObservableCollection<DomainBase> items;
}

但是,以下行无法编译,因为PersonRepository无法转换为IRepository&lt; DomainBase&gt;:

var viewModel = new DomainBaseViewModel<PersonRepository>();

虽然我可以通过协方差来解决这个问题但是它不允许在参数列表中使用type参数:

public interface IRepository<out T> where T : class
{
    ...
    T Persist(object item);
}

class PersonRepository : IRepository<Person>
{
    public Person Persist(object item)
    {
        ...
    }
}

所以我必须将参数转换为Person,这会危及类型安全。

在这种情况下,是否有更好的方法允许协方差和在参数列表中使用类型参数?

1 个答案:

答案 0 :(得分:2)

不 - 对协方差的限制的全部意义在于它保证了安全性。 PersonRepository 不是IRepository<DomainBase>,因为您不能要求它保留任意DomainBase个对象。你期望这段代码做什么?

class Product : DomainBase {}
...
IRepository<DomainBase> repository = new PersonRepository();
repository.Persist(new Product());

PersonRepository不知道如何保留Product值。

如果在某些情况下你只需要&#34;阅读&#34;存储库接口的一部分,你总是可以明确地调用它:

public interface IRepositoryReader<out T>
{
    IEnumerable<T> Fetch();
}

public interface IRepository<T> : IRepositoryReader<T>
{
    T Persist(T item);
}

然后你的DomainBaseViewModel课程可能是:

class DomainBaseViewModel<TRepository>
    where TRepository : IRepositoryReader<DomainBase>, new()
{
    private TRepository repository = new TRepository();
    private ObservableCollection<DomainBase> items;
}

如果您希望DomainBaseViewModel也能保留项目,那么这不起作用。在这种情况下,也许它在模型类型中应该是通用的:

class DomainBaseViewModel<TRepository, TEntity>
    where TRepository : IRepository<TEntity>, new()
{
    private TRepository repository = new Repository();
    private ObservableCollection<TEntity> items;
}

然后:

var viewModel = new DomainBaseViewModel<PersonRepository, Person>();