I have this generic singleton that looks like this:
public class Cache<T>
{
private Dictionary<Guid, T> cachedBlocks;
// Constructors and stuff, to mention this is a singleton
public T GetCache(Guid id)
{
if (!cachedBlocks.ContainsKey(id))
cachedBlocks.Add(id, LoadFromSharePoint(id))
return cachedBlocks[id];
}
public T LoadFromSharePoint(Guid id)
{
return new T(id) // Here is the problem.
}
}
The error message is:
Cannot create an instance of type T because it does not have the new() constraint.
I have to mention that I must pass that id
parameter, and there is no other way to do so. Any ideas on how to solve this would be highly appreciated.
Constructors are called only once at the time of the creation of the object.
You must be sure the type defines the constructor you want to invoke. You cannot constrain the generic type T to types that have a particular constructor other than the default constructor. (E.g. where T : new(Guid) does not work.)
Constructors are similar to methods and just like generic methods we can also have generic constructors in Java though the class is non-generic. Since the method does not have return type for generic constructors the type parameter should be placed after the public keyword and before its (class) name.
Generic constructors are the same as generic methods. For generic constructors after the public keyword and before the class name the type parameter must be placed. Constructors can be invoked with any type of a parameter after defining a generic constructor.
Normally you would constrain the type T
to a type that has a default constructor and call that. Then you'd have to add a method or property to be able to provide the value of id
to the instance.
public static T LoadFromSharePoint<T>(Guid id)
where T : new() // <-- Constrain to types with a default constructor
{
T value = new T();
value.ID = id;
return value;
}
Alternatively since you specify that you have to provide the id
parameter through the constructor, you can invoke a parameterized constructor using reflection. You must be sure the type defines the constructor you want to invoke. You cannot constrain the generic type T
to types that have a particular constructor other than the default constructor. (E.g. where T : new(Guid)
does not work.)
For example, I know there is a constructor new List<string>(int capacity)
on List<T>
, which can be invoked like this:
var type = typeof(List<String>);
object list = Activator.CreateInstance(type, /* capacity */ 20);
Of course, you might want to do some casting (to T
) afterwards.
To do this you should specify what T
is. Your Cache<T>
can hold anything? Tiger
, Fridge
and int
as well? That is not a sound design. You should constrain it. You need an instance of T
which will take a Guid
to construct the instance. That's not a generic T
. Its a very specific T
. Change your code to:
public class Cache<T> where T : Cacheable, new()
{
private Dictionary<Guid, T> cachedBlocks;
// Constructors and stuff, to mention this is a singleton
public T GetCache(Guid id)
{
if (!cachedBlocks.ContainsKey(id))
cachedBlocks.Add(id, LoadFromSharePoint(id))
return cachedBlocks[id];
//you're first checking for presence, and then adding to it
//which does the same checking again, and then returns the
//value of key again which will have to see for it again.
//Instead if its ok you can directly return
//return cachedBlocks[id] = LoadFromSharePoint(id);
//if your LoadFromSharePoint is not that expensive.
//mind you this is little different from your original
//approach as to what it does.
}
public T LoadFromSharePoint(Guid id)
{
return new T { Key = id }; // Here is no more problem.
}
}
public interface Cacheable
{
Guid Key { get; set; }
}
Now derive all the cacheables (whatever T
s that you will pass it for Cache<T>
) from the interface Cacheable
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With