Let’s assume I have:
- A generic method
Get<T> - A few interfaces
IEntity,IValue - A few classes that respectively implements those interfaces ex:
Entity->IEntity,Value->IValueetc.
=> 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:
- A generic method
Get<T>with a Type constraintwhere T: IPersistable(to prevent most of the types to be passed as a parameter) - The 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 are:
- It is not clean… I could live with that since there are only very few types to check from
- The signature does not match what the function really does. It allows an
IPersistablein, 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…
You should just create dedicated methods. The sample code with the
ifshows that your current method doesn’t do one thing. It does multiple.Just go with:
This is a lot cleaner on all layers:
GetEntityvs.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
Entityand one forValue.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)andservice.Entities.Get(42)