I have this type that contains two overloads of a generic method. I like to retrieve one of the overloads (with the Func<T>
parameter) using reflection. The problem however is that I can't find the correct parameter type to supply the Type.GetMethod(string, Type[])
method with.
Here is my class definition:
public class Foo
{
public void Bar<T>(Func<T> f) { }
public void Bar<T>(Action<T> a) { }
}
And this is what I've come up with, unfortunately without succ开发者_Python百科es:
[TestMethod]
public void Test1()
{
Type parameterType = typeof(Func<>);
var method = typeof(Foo).GetMethod("Bar", new Type[] { parameterType });
Assert.IsNotNull(method); // Fails
}
How can I get the MethodInfo
of a generic method of which I know the parameters?
Why don't you use expression trees? This makes it much easier:
public static MethodInfo GetMethod<T>(
Expression<Action<T>> methodSelector)
{
var body = (MethodCallExpression)methodSelector.Body;
return body.Method;
}
[TestMethod]
public void Test1()
{
var expectedMethod = typeof(Foo)
.GetMethod("Bar", new Type[] { typeof(Func<>) });
var actualMethod =
GetMethod<Foo>(foo => foo.Bar<object>((Func<object>)null)
.GetGenericMethodDefinition();
Assert.AreEqual(expectedMethod, actualMethod);
}
Surprisingly, it looks like you'll need to call GetMethods()
and loop over the methods until you find the one you want.
For example:
var yourMethod = typeof(Foo).GetMethods()
.First(m => m.Name == "Bar"
&& m.GetParameters().Length == 1
&& m.GetParameters()[0].ParameterType.ContainsGenericParameters
&& m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Func<>));
I don't think you can do this directly using GetMethod
. I suspect you'll have to iterate over all the methods called Bar
, then:
- Check that the method has one type parameter
- Check that the method has one normal parameter
- Use the type parameter to make a
Func<T>
(withtypeof(Func<>).MakeGenericType
) and check that the parameter type matches that.
LINQ is good for this sort of thing. Complete sample:
using System;
using System.Reflection;
using System.Linq;
public class Foo
{
public void Bar<T>(Func<T> f) { }
public void Bar<T>(Action<T> a) { }
}
class Test
{
static void Main()
{
var methods = from method in typeof(Foo).GetMethods()
where method.Name == "Bar"
let typeArgs = method.GetGenericArguments()
where typeArgs.Length == 1
let parameters = method.GetParameters()
where parameters.Length == 1
where parameters[0].ParameterType ==
typeof(Func<>).MakeGenericType(typeArgs[0])
select method;
Console.WriteLine("Matching methods...");
foreach (var method in methods)
{
Console.WriteLine(method);
}
}
}
Basically generics and reflection are really nasty in combination, I'm afraid :(
You need to specify a concrete type using MethodInfo.MakeGenericMethod.
However, I should point out, that getting the right type to invoke MakeGenericMethod
on is not easy when you have an overloaded generic method.
Here is an example:
var method = typeof(Foo)
.GetMethods()
.Where(x => x.Name == "Bar")
.Where(x => x.IsGenericMethod)
.Where(x => x.GetGenericArguments().Length == 1)
.Where(x => x.GetParameters().Length == 1)
.Where(x =>
x.GetParameters()[0].ParameterType ==
typeof(Action<>).MakeGenericType(x.GetGenericArguments()[0])
)
.Single();
method = method.MakeGenericMethod(new Type[] { typeof(int) });
Foo foo = new Foo();
method.Invoke(foo, new Func<int>[] { () => return 42; });
You'll struggle with just with just GetMethod - you could try something along the lines of;
var method = (from m in typeof(Foo).GetMethods()
where
m.IsGenericMethodDefinition == true &&
m.Name == "Bar" &&
m.GetParameters().Length > 0 &&
m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == parameterType
select m).FirstOrDefault();
精彩评论