Tim Bee
Tim Bee

Reputation: 2230

C# generic method with interface type constraint

Let's assume I have:

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

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

Upvotes: 3

Views: 4579

Answers (2)

Daniel Hilgarth
Daniel Hilgarth

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:

  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)

Upvotes: 5

Hossein Narimani Rad
Hossein Narimani Rad

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

Related Questions