变体通用接口
Variant Generic Interfaces
我有一个通用接口,还有一个 class 使用具体类型参数实现该接口。我也有一个泛型 class 使用泛型接口作为它的类型约束,但是类型参数被限制为某个基 class 的子 class。我想用实现该接口的 class 实例化通用 class,但在将 class 转换为该接口时遇到问题。以下代码说明了我提到的所有 classes:
基地class:
class DomainBase
{
}
接口中用作类型参数的class
class Person : DomainBase
{
}
通用接口:
public interface IRepository<T> where T : class
{
IEnumerable<T> Fetch();
T Persist(T item);
}
实现通用接口的class:
class PersonRepository : IRepository<Person>
{
public IEnumerable<Person> Fetch()
{
...
}
public Person Persist(Person item)
{
...
}
}
通用class使用通用接口:
class DomainBaseViewModel<Repository>
where Repository : IRepository<DomainBase>, new()
{
private Repository repository = new Repository();
private ObservableCollection<DomainBase> items;
}
但是,无法编译以下行,因为 PersonRepository 无法转换为 IRepository:
var viewModel = new DomainBaseViewModel<PersonRepository>();
虽然我可以通过协方差解决这个问题,但它不允许在参数列表中使用类型参数:
public interface IRepository<out T> where T : class
{
...
T Persist(object item);
}
class PersonRepository : IRepository<Person>
{
public Person Persist(object item)
{
...
}
}
所以我必须将参数转换为 Person,这会损害类型安全。
在这种情况下,是否有更好的方法允许协变和在参数列表中使用类型参数?
否 - 对协方差的限制的全部意义在于它保证了安全性。一个 PersonRepository
不是 一个 IRepository<DomainBase>
因为你不能要求它持久化任何任意的 DomainBase
对象。您希望这段代码做什么?
class Product : DomainBase {}
...
IRepository<DomainBase> repository = new PersonRepository();
repository.Persist(new Product());
PersonRepository
不知道如何保留 Product
值。
如果在某些情况下您只需要存储库界面的 "read" 部分,您总是可以显式调用它:
public interface IRepositoryReader<out T>
{
IEnumerable<T> Fetch();
}
public interface IRepository<T> : IRepositoryReader<T>
{
T Persist(T item);
}
那么你的 DomainBaseViewModel
class 可能是:
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>();
我有一个通用接口,还有一个 class 使用具体类型参数实现该接口。我也有一个泛型 class 使用泛型接口作为它的类型约束,但是类型参数被限制为某个基 class 的子 class。我想用实现该接口的 class 实例化通用 class,但在将 class 转换为该接口时遇到问题。以下代码说明了我提到的所有 classes:
基地class:
class DomainBase
{
}
接口中用作类型参数的class
class Person : DomainBase
{
}
通用接口:
public interface IRepository<T> where T : class
{
IEnumerable<T> Fetch();
T Persist(T item);
}
实现通用接口的class:
class PersonRepository : IRepository<Person>
{
public IEnumerable<Person> Fetch()
{
...
}
public Person Persist(Person item)
{
...
}
}
通用class使用通用接口:
class DomainBaseViewModel<Repository>
where Repository : IRepository<DomainBase>, new()
{
private Repository repository = new Repository();
private ObservableCollection<DomainBase> items;
}
但是,无法编译以下行,因为 PersonRepository 无法转换为 IRepository
var viewModel = new DomainBaseViewModel<PersonRepository>();
虽然我可以通过协方差解决这个问题,但它不允许在参数列表中使用类型参数:
public interface IRepository<out T> where T : class
{
...
T Persist(object item);
}
class PersonRepository : IRepository<Person>
{
public Person Persist(object item)
{
...
}
}
所以我必须将参数转换为 Person,这会损害类型安全。
在这种情况下,是否有更好的方法允许协变和在参数列表中使用类型参数?
否 - 对协方差的限制的全部意义在于它保证了安全性。一个 PersonRepository
不是 一个 IRepository<DomainBase>
因为你不能要求它持久化任何任意的 DomainBase
对象。您希望这段代码做什么?
class Product : DomainBase {}
...
IRepository<DomainBase> repository = new PersonRepository();
repository.Persist(new Product());
PersonRepository
不知道如何保留 Product
值。
如果在某些情况下您只需要存储库界面的 "read" 部分,您总是可以显式调用它:
public interface IRepositoryReader<out T>
{
IEnumerable<T> Fetch();
}
public interface IRepository<T> : IRepositoryReader<T>
{
T Persist(T item);
}
那么你的 DomainBaseViewModel
class 可能是:
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>();