Search code examples
c#genericsreflectioninvoke

Reflection: invoke a method with generic parameter


I everyone,

I have some issues with invoking a method with reflection.

The method sign is

public T Create<T, TK>(TK parent, T newItem, bool updateStatistics = true, bool silent = false)
        where T : class
        where TK : class;
    public T Create<T, TK>(TK parent, string newName, Language language = null, bool updateStatistics = true, bool silent = false)
        where T : class
        where TK : class;

I want to use the second overload.

My code is

typeof(ObjectType).GetMethod("Create")
            .MakeGenericMethod(new Type[] { typeof(Item), typeof(TKparent) })
            .Invoke(_objectInstance, new object[] { parent, name, _language, true, false });

where Item is a class, TKparent is a type variable and parent is a TKparent instance.

I get a System.Reflection.AmbiguousMatchException.

I think the problem is related to generics

I tried also this:

typeof(ObjectType).GetMethod("Create", new Type[] { typeof(TKparent), typeof(string), typeof(Globalization.Language), typeof(bool), typeof(bool) })
            .MakeGenericMethod(new Type[] { typeof(Item), typeof(Tparent) })
            .Invoke(_objectInstance, new object[] { parent, name, _language, true, false });

but in this case I get a System.NullReferenceException (no method found)

Can anyone help with this, I'm getting mad!

thank you


Solution

  • The problem is that GetMethod finds multiple methods with that name before you even tell it which overload you want. The overload of GetMethod that allows you to pass in an array of types works for non-generic methods, but since the parameters are generic you can't use it.

    You need to call GetMethods and filter down to the one you want:

    var methods = typeof(ObjectType).GetMethods();
    
    var method = methods.Single(mi => mi.Name=="Create" && mi.GetParameters().Count()==5);
    
    method.MakeGenericMethod(new Type[] { typeof(Item), typeof(TKparent) })
          .Invoke(_objectInstance, new object[] { parent, name, _language, true, false });
    

    You could obviously inline all of that if you want, but it makes debugging easier if you separate it into separate lines.