Reputation: 2230
Let's assume I have:
Get<T>
IEntity
, IValue
Entity
-> IEntity
, Value
-> IValue
etc.=> Is there a way for the Get<T>
method to allow the interfaces only as generic Types?
Get<IEntity>(42); //Allowed
Get<Entity>(42); //Compiler error
My current solution looks like this:
Get<T>
with a Type constraint where T: IPersistable
(to prevent most of the types to be passed as a parameter)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 are:
IPersistable
in, but not really <- that really bugs me :(Edit: I'm considering such constraints to avoid surpopulation of my class.
I have something like 8 or 9 generic methods in that class that all pretty much work this way. The intuitive way of doing would be as @DanielHilgarth suggested to have 1 method per type only. The methods currently can be called with 4 or 5 types only. But still, that would mean 32-40 methods in that class.
If possible I'd like to avoid that.
Edit2: the need for preventing "real" classes to be called comes from a covariance/contravariance problem. The EntityDao and ValueDao Get<T>
methods return IEntity
and IValue
objects. What works fine when I query for a single object fails when I call for a collection in a GetAll<T>
methods since I cannot cast an IEnumerable<IValue>
in an IEnumerable<Value>
.
I just noticed this answer from @JonSkeets about casting of lists. That could be a workaround...
Upvotes: 3
Views: 4579
Reputation: 174299
You should just create dedicated methods. The sample code with the if
shows that your current method doesn't do one thing. It does multiple.
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 a lot cleaner on all layers:
GetEntity
vs. Get<IEntity>
If this will result in too many similar methods on your service, it is time to break out new classes, e.g. one for Entity
and one for Value
.
Then you could give your service properties that return the new classes. That's the same I am doing when implementing my query objects. It could then look like this: service.Values.Get(13)
and service.Entities.Get(42)
Upvotes: 5
Reputation: 32481
This may 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);
}
}
The your Get
method will be something like this:
public T Get<T>(long id) // maybe you restrict the T to something
{
Persistable<T>.Get(id);
}
Upvotes: 1