C # generic method with interface type constraint
Suppose I have:
- General method
Get<T>
- Multiple interfaces
IEntity
,IValue
- Several classes that respectively implement these interfaces ex:
Entity
→IEntity
,Value
→IValue
etc.
=> Is there a way for a method Get<T>
to only allow interfaces as generic types?
Get<IEntity>(42); //Allowed
Get<Entity>(42); //Compiler error
My current solution looks like this:
- A generic
Get<T>
type constrained methodwhere T: IPersistable
(to prevent most types from being passed as a parameter) - Interfaces implement
IPersistable
The function actively checks the type:
public T Get<T>(long id) where T : IPersistable
{
if (typeof (T) == typeof (IEntity))
return (T) EntityDao.Get(id);
if (typeof (T) == typeof (IValue))
return (T) ValueDao.Get(id);
//...
throw new TechnicalException("Type not supported");
}
=> The problem is this:
- It's not clean ... I could live with this because there are only very few types to check from
- The signature does not match the function's action. It allows
IPersistable
in, but not really <- , which really worries me :(
Edit: I am considering such restrictions to avoid overpopulating my class.
I have something like 8 or 9 generic methods in this class that all work so well. An intuitive way to work would be like @DanielHilgarth suggested using only one method per type. Currently, methods can only be called with 4 or 5 types. But still, this will mean 32-40 methods in this class.
If possible, I would like to avoid this.
Edit2: The need to prevent "real" classes from being called occurs due to a covariance / contravariance issue. The EntityDao and ValueDao methods Get<T>
return objects IEntity
and IValue
. What works great when I query for a single object fails when I call the collection in methods GetAll<T>
as I cannot use IEnumerable<IValue>
in IEnumerable<Value>
.
I just noticed this answer from @JonSkeets about list casting. This could be a workaround ...
source to share
You just have to create custom methods. Sample code c if
shows that your current method does nothing. These are several.
Just go with:
GetEntity(42);
GetValue(13);
public IEntity GetEntity(long id)
{
return EntityDao.Get(id);
}
public IValue GetValue(long id)
{
return ValueDao.Get(id);
}
This is much cleaner on all layers:
-
GetEntity
vs.Get<IEntity>
- You communicate clearly what is possible. You have no runtime exceptions.
- Your get methods don't require type switching.
If that leads to too many similar methods in your service, it's time to rip out new classes, for example. one for Entity
and one for Value
. Then you can provide your own service properties that return new classes. I do the same when implementing my request objects . It might look like this: service.Values.Get(13)
andservice.Entities.Get(42)
source to share
This could be an alternative:
abstract class Persistable<T>
{
protected static Func<long, T> mapFunction;
public static T Get(long id)
{
return mapFunction(id);
}
}
class Entity : Persistable<Entity>
{
public static Entity()
{
Persistable<Entity>.mapFunction = input => EntityDao.Get(input);
}
}
class Value : Persistable<Value>
{
public static Value()
{
Persistable<Value>.mapFunction = input => ValueDao.Get(input);
}
}
Your method Get
will look something like this:
public T Get<T>(long id) // maybe you restrict the T to something
{
Persistable<T>.Get(id);
}
source to share