3

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 -> 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:

  • A generic method Get<T> with a Type constraint where 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:

  1. It is not clean... I could live with that since there are only very few types to check from
  2. The signature does not match what the function really does. It allows an 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...

3
  • Could you explain, why do you need such constraint? Commented Jan 29, 2013 at 10:05
  • Sure, I just edited the question. Commented Jan 29, 2013 at 10:12
  • That's an explanation, why do you want to make generic method isntead of number of factory methods. It would be helpful, if you explain, why this method should be constrained with interfaces types only. Commented Jan 29, 2013 at 10:20

2 Answers 2

5

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:

  1. GetEntity vs. Get<IEntity>
  2. You clearly communicate what is possible. You don't have any runtime exceptions.
  3. Your get methods don't need any type switching.

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)

Sign up to request clarification or add additional context in comments.

5 Comments

I could but then I fear for an 'overpopulation' of methods in my class. I have something like 8 or 9 generic methods all work on a similar way in that class. If I exploded my generic methods, I would land with something like 40 methods. If possible I'd like to avoid that.
@TimBourguignon: In that case you should break out some new classes.
That's indeed a possibility... that I've been trying to avoid since a single entrypoint in that service really is convenient (all the mentionned methods are getters, so in a way it makes sense to have them all grouped together)...
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 than look like this: service.Values.Get(13) and service.Entities.Get(42).
I have to ponder it a bit, but that's a good idea, it sure looks sexy :D
1

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);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.