简短:
为什么要将派生类型添加到集合中,但是当尝试添加派生类型的泛型时,它会失败?
“简短”代码:
//a generic repository
public class EfRepository<T> : IRepository<T> where T: BaseCatalogModel{...}
public CatalogRepository(IRepository<Product> productRepository, IRepository<Category> categoryRepository)
{
//This passes
Dictionary<int, BaseCatalogModel> dic1 = new Dictionary<int, BaseCatalogModel>();
dic1.Add(1, new Product());
dic1.Add(2, new Category());
dic1.Add(3, new BaseCatalogModel());
//This not.
//The error: cannot convert from 'YoYo.Core.Data.Repositories.EfRepository<YoYo.Commerce.Common.Domain.Catalog.Product>'
//to 'YoYo.Core.Data.Repositories.EfRepository<YoYo.Commerce.Common.Domain.Catalog.BaseCatalogModel>'
Dictionary<int, EfRepository<BaseCatalogModel>> dic2 = new Dictionary<int, EfRepository<BaseCatalogModel>>();
dic2.Add(1, new EfRepository<Product>());
dic2.Add(2, new EfRepository<Category>());
}
长期交易: 在一个在线商店工作,我想在目录库中保存与管理目录相关的所有存储库的集合。
我们的想法是从一个存储库管理整个目录。
存储库集合的类型为Dictionary)
我无法将任何BaseCatalogModel派生类型存储库添加到集合中。
我很乐意在上述方面获得任何帮助或建议更好的实施。
public class BaseCatalogModel
{
public int Id { get; set; }
...
}
public class Category:BaseCatalogModel
{
...
}
public class Product : BaseCatalogModel
{
...
}
public class CatalogRepository : ICatalogRepository
{
private readonly Dictionary<Type, IRepository<BaseEntity>> _repositoriesCollection= new Dictionary<Type, IRepository<BaseEntity>>();
public CatalogRepository(IRepository<Product> productRepository, IRepository<Category> categoryRepository)
{
_repositoriesCollection.Add(typeof(Category), categoryRepository); //==> this fails
_repositoriesCollection.Add(typeof(Product), productRepository); //==> this fails
}
public T GetCatalogItem<T>(int id) where T : BaseCatalogModel
{
//returns a catalog item using type and id
}
public IEnumerable<T> GetCatalogItem<T>() where T : BaseCatalogModel
{
//returns the entire collection of catalog item
}
}
答案 0 :(得分:1)
所以这是泛型相当普遍的问题。
想象2个班class Base
和class A
。 class A
来自Base
。
public class Base { }
public class A : Base { }
现在考虑List<T>
。您可以从List<T>
创建类以保留Base或A:
List<Base> x = new List<Base>();
和
List<A> y = new List<A>();
一个常见的误解是y的类必须是x类的后代,但这不可能是真的,因为x有Add(Base item)
之类的方法,y有Add(A item)
之类的方法并且编译器不可能保证接口将与x的接口兼容。这是因为如果将List<A>
的实例视为List<Base>
的实例,则没有什么可以阻止使用Base的实例或Base的另一个子类调用Add。
现在,界面的某些部分可以保证兼容。它们是返回A类实例的任何部分,因为A总是可以代替Base。
如果您的界面仅输出通用,并且您使用的是.net 4,则可以使用简单的解决方案。 out泛型修饰符:
public class Example
{
private readonly Dictionary<Type, IRepository<Base>> _repositoriesCollection =
new Dictionary<Type, IRepository<Base>>();
public void DoSomething()
{
_repositoriesCollection.Add(typeof(A), new Repository<A>());
}
}
interface IRepository<out T> where T : Base
{
T MakeSomeItem(string info);
//void AddSomeItem(string info, T itemToAdd); <- this will not
// work because T
// is out - so can't
// go in...
IEnumerable<T> MakeSomeListOfItems(); // This is OK because
// IEnumerable<T> is declared as IEnumerable<out T> in the fx
//List<T> Something(); <- not ok because List<T> is not List<out T>
}
public class Repository<T> : IRepository<T> where T : Base
{
public T MakeSomeItem(string info)
{
throw new NotImplementedException();
}
public IEnumerable<T> MakeSomeListOfItems()
{
throw new NotImplementedException();
}
}
public class Base { }
public class A : Base { }
此解决方案不适用于2个案例;当你需要将一个项目传递到你的界面时,以及当你不使用.net 4时。
对于这两种情况,还有许多不同的解决方案。
1)我也需要将一个项目传递给接口,我正在使用.net 4 - 只需将其作为Base传递,如果你需要维护类型安全性,请使用其他地方的泛型方法。
interface IRepository<out T> where T : Base
{
T MakeSomeItem(string info);
void AddSomeItem(string info, Base itemToAdd);
}
public class Repository<T> : IRepository<T> where T : Base
{
public T MakeSomeItem(string info){ throw new NotImplementedException(); }
public void AddSomeItem(string info, Base itemToAdd)
{
T castedItem = (T) itemToAdd; //fails here at
//run time if not
// correct type
AddSomeItem(info, itemToAdd);
}
public void AddSomeItem(string info, T itemToAdd)
{
/// do it for real...
}
}
2)如果您不使用.net 4,那么您还可以执行其他操作,强制存储库实现接口的Base版本:
interface IRepository<T> where T : Base
{
T MakeSomeItem(string info);
void AddSomeItem(string info, T itemToAdd)
}
public class Repository<T> : IRepository<Base>, IRepository<T> where T : Base
{
public T MakeSomeItem(string info) { throw new NotImplementedException(); }
public void AddSomeItem(string info, Base itemToAdd)
{
T castedItem = (T) itemToAdd; //fails here at
//run time if not
// correct type
AddSomeItem(info, itemToAdd);
}
public void AddSomeItem(string info, T itemToAdd)
{
/// do it for real...
}
Base IRepository<Base>.MakeSomeItem(string info)
{
return MakeSomeItem(info);
}
}
如果你想保持输入的强力输入,你还可以做更多的事情 - 但我认为现在我的答案已经足够长了。