更新 2:嗯,你在评论中说你没有定义非泛型 CacheCollection 类型;但是你接着说你有一个Dictionary<Type, CacheCollection>。这些陈述不可能都是真的,所以我猜CacheCollection 你的意思是CacheCollection<EntityBase>。
现在问题来了:如果X<T> 类型为is not covariant,则X<Derived> 不能转换为X<Base>。也就是说,在您的情况下,仅仅因为 T 派生自 EntityBase 并不意味着 CacheCollection<T> 派生自 CacheCollection<EntityBase>。
要具体说明为什么会这样,请考虑List<T> 类型。假设您有一个List<string> 和一个List<object>。 string 派生自object,但并不意味着List<string> 派生自List<object>;如果是这样,那么你可以有这样的代码:
var strings = new List<string>();
// If this cast were possible...
var objects = (List<object>)strings;
// ...crap! then you could add a DateTime to a List<string>!
objects.Add(new DateTime(2010, 8, 23));
幸运的是,解决这个问题的方法(在我看来)非常简单。基本上,通过定义一个 非泛型基类,CacheCollection<T> 将从该基类中派生,遵循我最初的建议。更好的是,使用简单的非通用接口。
interface ICacheCollection
{
EntityBase Item(int id);
}
(查看下面我更新的代码,了解如何在泛型类型中实现此接口)。
然后对于您的字典,而不是 Dictionary<Type, CacheCollection<EntityBase>>,将其定义为 Dictionary<Type, ICacheCollection>,然后您的其余代码应该放在一起。
更新:您似乎在隐瞒我们!所以你有一个非泛型的 CacheCollection 基类,CacheCollection<T> 派生自它,对吗?
如果我对您对此答案的最新评论的理解是正确的,这是我给您的建议。编写一个类来提供对您的Dictionary<Type, CacheCollection> 的间接访问。这样,您可以拥有许多 CacheCollection<T> 实例,而不会牺牲类型安全性。
类似这样的东西(注意:代码根据上面的新更新修改):
class GeneralCache
{
private Dictionary<Type, ICacheCollection> _collections;
public GeneralCache()
{
_collections = new Dictionary<Type, ICacheCollection>();
}
public T GetOrAddItem<T>(int id, Func<int, T> factory) where T : EntityBase
{
Type t = typeof(T);
ICacheCollection collection;
if (!_collections.TryGetValue(t, out collection))
{
collection = _collections[t] = new CacheCollection<T>(factory);
}
CacheCollection<T> stronglyTyped = (CacheCollection<T>)collection;
return stronglyTyped.Item(id);
}
}
这将允许您编写如下代码:
var cache = new GeneralCache();
RedEntity red = cache.GetOrAddItem<RedEntity>(1, id => new RedEntity(id));
BlueEntity blue = cache.GetOrAddItem<BlueEntity>(2, id => new BlueEntity(id));
好吧,如果T 派生自EntityBase 但没有无参数构造函数,那么最好的办法是指定一个工厂方法,该方法将为CacheCollection<T> 中的适当参数生成T构造函数。
像这样(注意:代码根据上面的新更新修改):
public class CacheCollection<T> : List<CacheItem<T>>, ICacheCollection where T : EntityBase
{
private Func<int, T> _factory;
public CacheCollection(Func<int, T> factory)
{
_factory = factory;
}
// Here you can define the Item method to return a more specific type
// than is required by the ICacheCollection interface. This is accomplished
// by defining the interface explicitly below.
public T Item(int id)
{
// Note: use FirstOrDefault, as First will throw an exception
// if the item does not exist.
CacheItem<T> result = this.Where(t => t.Entity.Id == id)
.FirstOrDefault();
if (result == null) //item not yet in cache, load it!
{
T entity = _factory(id);
// Note: it looks like you forgot to instantiate your result variable
// in this case.
result = new CacheItem<T>(entity);
Add(result);
}
return result.Entity;
}
// Here you are explicitly implementing the ICacheCollection interface;
// this effectively hides the interface's signature for this method while
// exposing another signature with a more specific return type.
EntityBase ICacheCollection.Item(int id)
{
// This calls the public version of the method.
return Item(id);
}
}
我还建议,如果您的项目将有唯一的 ID,使用 Dictionary<int, CacheItem<T>> 作为您的后备存储而不是 List<CacheItem<T>>,因为它会使您的项目查找 O(1) 而不是 O(N )。
(我还建议使用私有成员来实现这个类来保存集合本身,而不是直接从集合继承,因为使用继承会暴露您可能想要隐藏的功能,例如Add、Insert 等)