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

Community
  • 1
  • 1
Timothée Bourguignon
  • 2,190
  • 3
  • 23
  • 39

2 Answers2

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)

Community
  • 1
  • 1
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • 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. – Timothée Bourguignon Jan 29 '13 at 10:07
  • 1
    @TimBourguignon: In that case you should break out some new classes. – Daniel Hilgarth Jan 29 '13 at 10:09
  • 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)... – Timothée Bourguignon Jan 29 '13 at 10:24
  • 1
    You could give your service properties that return the new classes. That's the same I am doing when implementing [my query objects](http://stackoverflow.com/a/9943907/572644). It could than look like this: `service.Values.Get(13)` and `service.Entities.Get(42)`. – Daniel Hilgarth Jan 29 '13 at 10:31
  • I have to ponder it a bit, but that's a good idea, it sure looks sexy :D – Timothée Bourguignon Jan 29 '13 at 10:47
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);
}
Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116