2010-12-14 10 views
9

Mam ten typ, który zawiera dwa przeciążenia metody ogólnej. Chciałbym pobrać jedno z przeciążeń (z parametrem Func<T>) za pomocą odbicia. Problem polega jednak na tym, że nie mogę znaleźć właściwego typu parametru, który zapewni metodę Type.GetMethod(string, Type[]).Wywołanie metodyInformacja o prawidłowym przeciążeniu metody ogólnej

Oto moja definicja klasy:

public class Foo 
{ 
    public void Bar<T>(Func<T> f) { } 
    public void Bar<T>(Action<T> a) { } 
} 

A to co mam wymyślić, niestety bez sukcesu:

[TestMethod] 
public void Test1() 
{ 
    Type parameterType = typeof(Func<>); 

    var method = typeof(Foo).GetMethod("Bar", new Type[] { parameterType }); 

    Assert.IsNotNull(method); // Fails 
} 

Jak mogę uzyskać MethodInfo z ogólnej metody które znam parametry?

Odpowiedz

9

Dlaczego nie używasz drzewek wyrażeń? To znacznie ułatwia:

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); 
} 
+0

Wow .. to jest fajne.Działa świetnie! I tak mało kodu. – Anne

1

Musisz określić rodzaj betonu przy użyciu MethodInfo.MakeGenericMethod.

Muszę jednak podkreślić, że uzyskanie odpowiedniego rodzaju powołać MakeGenericMethod nie jest proste, gdy masz przeciążone metody rodzajowe.

Oto przykład:

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; }); 
+0

To nie pomoże. Nie może uzyskać otwartej instancji. – SLaks

+0

Czy możesz pokazać mi przykład? – Anne

+0

@SLaks: Tak, jest to możliwe. Napiszę przykład. – jason

1

ja nie myśleć można to zrobić bezpośrednio z GetMethod. Podejrzewam, że będziesz musiał iteracyjne nad wszystkich metod zwanych Bar, a następnie:

  • Sprawdź, czy metoda ma jeden parametr typu
  • Sprawdź, czy metoda ma jeden normalny parametr
  • użyć parametru typu do zrób Func<T> (z typeof(Func<>).MakeGenericType) i sprawdź, czy typ parametru pasuje do tego.

LINQ jest dobre dla tego rodzaju rzeczy. Cała próbka:

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); 
     } 
    } 
} 

Zasadniczo rodzajowych i refleksji są naprawdę paskudne w połączeniu, obawiam :(

4

Co ciekawe, wygląda na to trzeba zadzwonić GetMethods() i pętlę nad sposobami, aż znajdziesz jeden chcesz

na przykład:

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<>)); 
+0

To działa. Dzięki. – Anne

0

będziesz walczyć z tylko z tylko getMethod - można spróbować czegoś wzdłuż lin. es;

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(); 
Powiązane problemy