4

Can I use a type from reflection as a type parameter? E.g. I want to pick a persister based on a passed object:

IPersister GetPersisterFor(IEntity entity)
{
    return GetPersisterFor<entity.GetType()>(); // <-- this cannot be compiled
}

IPersister GetPersisterFor<TEntity>() where TEntity : IEntity
{
    //some logic to get persister...
}
Louis Rhys
  • 34,517
  • 56
  • 153
  • 221

3 Answers3

4

Only via reflection; you would need to use GetMethod to get the MethodInfo for the generic method, then call MakeGenericMethod(entity.GetType()).Invoke(this, null); on that.

However, easier is to cheat via dynamic:

IPersister Evil<T>(T obj) where T : IEntity {
    return GetPersisterFor<T>();
}

And make the first method just:

return Evil((dynamic)entity);

This is then a dynamic expression which will detect the correct T to use (to call Evil-of-T) for you.

Note: the only reason you need an extra method is to make sure it doesn't resolve back to itself recursively, since the names are the same.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • any reason why you call it Evil? :) – Louis Rhys Dec 26 '12 at 13:27
  • @Rhys because I needed to call it something :) – Marc Gravell Dec 26 '12 at 14:24
  • +1 Alternatively, you could just push the generic type parameter `T` up the call tree. At some point, the exact type of `entity` is probably known, so if all methods from that point down to `GetPersisterFor` retained `T` as a generic type, you wouldn't need to use `dynamic`. If that isn't possible (the top-level caller only has a variable of type `IYo` available), then this is a great shortcut to the `MakeGenericMethod` solutions. – jam40jeff Dec 26 '12 at 16:45
  • +1 Cool. I didn't know the dynamic type can be used like this :) – Louis Rhys Dec 27 '12 at 01:49
1

Yes, you need to get the generic method definition. After that you can use MethodInfo.MakeGenericMethod to construct the generic method.

So something like:

MethodInfo genericMethodDefinition = GetType()
    .GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
    .Where(method => method.IsGenericMethod && method.Name == "GetPersisterFor")
    .First();

// OR

MethodInfo genericMethodDefinition = GetType().GetMethod("GetPersisterFor",
    BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

// THEN

MethodInfo genericMethod = genericMethodDefinition.MakeGenericMethod(entity.GetType());
genericMethod.Invoke(this, null);
Nick Hill
  • 4,867
  • 3
  • 23
  • 29
  • You'll need a bit more in GetMethod, to a: access non-public methods, and b: disambiguate between the generic and non-generic method of the same name – Marc Gravell Dec 26 '12 at 10:37
1

Because both generic and non generic methods have same name, you have to iterate over all methods of class to find proper one, and then invoke it:

public IPersister GetPersisterFor(IEntity entity)
{       
    MethodInfo getPersisterForGenericMethod = 
                    GetType().GetMethods()
                        // iterate over all methods to find proper generic implementation
                        .Single(methodInfo => methodInfo.Name == "GetPersisterFor" && methodInfo.IsGenericMethod)
                        // supply it with generic type parameter
                        .MakeGenericMethod(entity.GetType());

    // invoke it
    return getPersisterForGenericMethod.Invoke(this, null) as IPersister;
}

public IPersister GetPersisterFor<TEntity>() where TEntity : IEntity
{
    return null;
}

ps: full source code available at gist.github

Akim
  • 8,469
  • 2
  • 31
  • 49