I'd like to get a MethodInfo
of a method from a generic class having a type parameter known only at runtime.
Here is how I would get a MethodInfo
for a generic method from a non-generic class:
class MyClass
{
public void MyMethod<T> (T arg)
{
}
}
static MethodInfo Resolve (Type type)
{
Expression<Action<MyClass, object>> lambda = (c, a) => c.MyMethod (a);
MethodCallExpression call = lambda.Body as MethodCallExpression;
return call
.Method // Get MethodInfo for MyClass.MyMethod<object>
.GetGenericMethodDefinition () // Get MethodInfo for MyClass.MyMethod<>
.MakeGenericMethod (type); // Get MethodInfo for MyClass.MyMethod<int>
}
Resolve (typeof (int)).Invoke (new MyClass (), new object[] {3});
Now if I want to try something similar with a generic class:
class MyClass<T>
{
public void MyMethod (T arg)
{
}
}
static MethodInfo Resolve (Type type)
{
Expression<Action<MyClass<object>, object>> lambda = (c, a) => c.MyMethod (a);
MethodCallExpression call = lambda.Body as MethodCallExpression;
return call
.Method // Get MethodInfo for MyClass<object>.MyMethod
.SomeMagicMethod (); // FIXME: how can I get a MethodInfo
// for MyClass<T>.MyMethod where typeof (T) == type?
}
Resolve (typeof (string)).Invoke (new MyClass<string> (), new object[] {"Hello, World!"});
Is it possible?
You need to use reflection to get the method to start with, then "construct" it by supplying type arguments with MakeGenericMethod: MethodInfo method = typeof(Sample). GetMethod(nameof(Sample. GenericMethod)); MethodInfo generic = method.
To call a generic method, you need to provide types that will be used during the method invocation. Those types can be passed as an instance of NType objects initialized with particular . NET types. Let's assume you have following .
To invoke a static method using its MethodInfo object, pass null for obj . If this method overload is used to invoke an instance constructor, the object supplied for obj is reinitialized; that is, all instance initializers are executed. The return value is null .
To call a method via reflection, use the Invoke() method that is contained in MethodInfo. Here, obj is a reference to the object on which the method is invoked. For static methods, you can pass null to obj. Any arguments for the method are specified in the array parameters.
public class MyClass<T>
{
public void MyMethod(T arg, bool flag)
{
Console.WriteLine("type: MyClass<{0}>, arg: {1}, flag:{2}", typeof(T),
arg.ToString(), flag);
}
public void MyMethod(T arg)
{
Console.WriteLine("type: MyClass<{0}>, arg: {1}", typeof(T), arg.ToString());
}
}
public class GenericInvokeTest
{
static MethodInfo Resolve(Type type)
{
var name = ActionName<object>(x => (o) => x.MyMethod(o));
var genericType = typeof(MyClass<>).MakeGenericType(new[] { type });
MethodInfo genericTypeMyMethodInfo = genericType.GetMethod(name); // "MyMethod");
genericTypeMyMethodInfo = genericType.GetMethod(name, new[] { type, typeof(bool) });
return genericTypeMyMethodInfo;
}
public static void Test1()
{
Resolve(typeof(string))
.Invoke(new MyClass<string>(), new object[] { "Hello, World!", true });
// Resolve(typeof(string))
.Invoke(new MyClass<string>(), new object[] { "Hello, World!" });
}
}
To make it strong-typed you should simplify and use different approach:
1) Get the name
of the action / method using expressions...
var name = ActionName<object>(x => (o) => x.MyMethod(o));
2) Then do the unavoidable reflection part
var genericType = typeof(MyClass<>).MakeGenericType(new[] { type });
MethodInfo genericTypeMyMethodInfo = genericType.GetMethod(name); // "MyMethod");
ActionName
is taking similar approach as e.g. OnPropertyChanged(x => x.Property)
public static string ActionName<T>(Expression<Func<MyClass<T>, Action<T>>> expression)
{
return GetMemberName(expression.Body);
}
public static string GetMemberName(Expression expression)
{
switch (expression.NodeType)
{
case ExpressionType.Lambda:
var lambdaExpression = (LambdaExpression)expression;
return GetMemberName(lambdaExpression.Body);
case ExpressionType.MemberAccess:
var memberExpression = (MemberExpression)expression;
var supername = GetMemberName(memberExpression.Expression);
if (String.IsNullOrEmpty(supername))
return memberExpression.Member.Name;
return String.Concat(supername, '.', memberExpression.Member.Name);
case ExpressionType.Call:
var callExpression = (MethodCallExpression)expression;
return callExpression.Method.Name;
case ExpressionType.Convert:
var unaryExpression = (UnaryExpression)expression;
return GetMemberName(unaryExpression.Operand);
case ExpressionType.Parameter:
return String.Empty;
default:
throw new ArgumentException(
"The expression is not a member access or method call expression");
}
}
Working solution:
static MethodInfo Resolve (Type type)
{
Expression<Action<MyClass<object>, object>> lambda = (c, a) => c.MyMethod (a);
MethodCallExpression call = lambda.Body as MethodCallExpression;
MethodInfo[] methods;
Type target;
target = call
.Method // Get MethodInfo for MyClass<object>.MyMethod
.DeclaringType // Get typeof (MyClass<object>)
.GetGenericTypeDefinition () // Get typeof (MyClass<>)
.MakeGenericType (type); // Get typeof (MyClass<T>) where typeof (T) == type
methods = target.GetMethods (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); // We probably don't need static methods
return Array.Find (methods, (m) => m.MetadataToken == method.MetadataToken); // Find MyClass<T>.MyMethod where typeof (T) == type
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With