I’m currently working on a generic collection class and I’d like to create a method which returns an object from the collection. If the specific object does not exist in the collection then the object should be created, added to the collection, and then returned.
I’m encountering a few problems though. The generic collection if of a type which represents an abstract class and I’m having trouble instantiating that.
Here’s my class definition:
public class CacheCollection<T> : List<CacheItem<T>> where T : EntityBase
And here’s the partially complete method on which I am working:
public T Item(int Id)
{
CacheItem<T> result = this.Where(t => t.Entity.Id == Id).First();
if (result == null) //item not yet in cache, load it!
{
T entity = new T(Id); //design time exception here: Cannot create an instance of the variable type 'T' because it does not have the new() constraint
result = new CacheItem<T>(entity);
this.Add(result);
}
return result.Entity;
}
Any ideas on how to get around this?
EDIT: All classes derived from EntityBase have Id as a read-only property.
UPDATE 2: Well, you say in a comment that you have not defined a non-generic
CacheCollectiontype; but then you go on to say that you have aDictionary<Type, CacheCollection>. These statements cannot both be true, so I am guessing that byCacheCollectionyou meanCacheCollection<EntityBase>.Now here’s the problem: a
X<Derived>cannot be cast to aX<Base>if theX<T>type is not covariant. That is, in your case, just becauseTderives fromEntityBasedoes not mean thatCacheCollection<T>derives fromCacheCollection<EntityBase>.For a concrete illustration of why this is, consider the
List<T>type. Say you have aList<string>and aList<object>.stringderives fromobject, but it does not follow thatList<string>derives fromList<object>; if it did, then you could have code like this:Fortunately, the way around this (in my view) is pretty straightforward. Basically, go with my original suggestion by defining a non-generic base class from which
CacheCollection<T>will derive. Better yet, go with a simple non-generic interface.(Take a look at my updated code below to see how you can implement this interface in your generic type).
Then for your dictionary, instead of a
Dictionary<Type, CacheCollection<EntityBase>>, define it as aDictionary<Type, ICacheCollection>and the rest of your code should come together.UPDATE: It seems that you were withholding from us! So you have a non-generic
CacheCollectionbase class from whichCacheCollection<T>derives, am I right?If my understanding of your latest comment to this answer is correct, here’s my advice to you. Write a class to provide indirect access to this
Dictionary<Type, CacheCollection>of yours. This way you can have manyCacheCollection<T>instances without sacrificing type safety.Something like this (note: code modified based on new update above):
This would allow you to write code like the following:
Well, if
Tderives fromEntityBasebut does not have a parameterless constructor, your best bet is going to be to specify a factory method that will generate aTfor the appropriate parameters in yourCacheCollection<T>constructor.Like this (note: code modified based on new update above):
I would also recommend, if your items are going to have unique IDs, to use a
Dictionary<int, CacheItem<T>>as your backing store instead of aList<CacheItem<T>>as it will make your item lookup O(1) instead of O(N).(I would also recommend implementing this class using a private member to hold the collection itself rather than inheriting from the collection directly, as using inheritance exposes functionality you probably want hidden such as
Add,Insert, etc.)