2009-12-11 2 views
2

Как найти общий перегруженный метод? Например, Queryable «sПоиск и вызов общего перегруженного метода

public static IQueryable<TResult> Select<TSource , TResult> (this IQueryable<TSource> source , Expression<Func<TSource , int , TResult>> selector); 

Я искал существующие решения, и они либо не достаточно общие (основаны на параметрах метода посчитает, и т.д.), нужно больше параметров, чем у меня есть (требуется общие определения типов или аргументы) или просто неправильно (не учитывают вложенные генерики и т. д.)

У меня есть тип определяющего класса - Type type, имя метода - string name и массив типов параметров (не общие определения) - Type[] types.

До сих пор, кажется, я должен сопоставьте каждый потенциальный метода .GetGenericArguments() к определенному типу путем сравнения (общий тип дерева?) Из метода .GetParameters().Select (p=>p.ParameterType) с помощью соответствующего пункта в types массиве, таким образом, выводя общие аргументы способ, поэтому я могу .MakeGenericMethod это.

Это кажется слишком сложным для задачи, поэтому, возможно, я переусердствую все это.

Любая помощь?

ответ

1

Итак, я просто закодировал это, что по сути является логическим типом вывода, и я думаю, что должен делать то, что мне нужно.

public static class TypeExtensions { 

    public static Type GetTypeDefinition (this Type type) { 
     return type.IsGenericType ? type.GetGenericTypeDefinition() : type; 
    } 

    public static IEnumerable<Type> GetImproperComposingTypes (this Type type) { 
     yield return type.GetTypeDefinition(); 
     if (type.IsGenericType) { 
      foreach (var argumentType in type.GetGenericArguments()) { 
       foreach (var t in argumentType.GetImproperComposingTypes()) yield return t; 
      } 
     } 
    } 

    private static Dictionary<Type , Type> GetInferenceMap (ParameterInfo[] parameters , Type[] types) { 
     var genericArgumentsMap = new Dictionary<Type , Type>(); 
     var match = parameters.All (parameter => parameter.ParameterType.GetImproperComposingTypes().Zip (types[parameter.Position].GetImproperComposingTypes()).All (a => { 
      if (!a.Item1.IsGenericParameter) return a.Item1 == a.Item2; 
      if (genericArgumentsMap.ContainsKey (a.Item1)) return genericArgumentsMap[a.Item1] == a.Item2; 
      genericArgumentsMap[a.Item1] = a.Item2; 
      return true; 
     })); 
     return match ? genericArgumentsMap : null; 
    } 

    public static MethodInfo MakeGenericMethod (this Type type , string name , Type[] types) { 
     var methods = from method in type.GetMethods() 
         where method.Name == name 
         let parameters = method.GetParameters() 
         where parameters.Length == types.Length 
         let genericArgumentsMap = GetInferenceMap (parameters , types) 
         where genericArgumentsMap != null 
         where method.GetGenericArguments().Length == genericArgumentsMap.Keys.Count() 
         select new { 
          method , 
          genericArgumentsMap 
         }; 
     return methods.Select (m => m.method.IsGenericMethodDefinition ? m.method.MakeGenericMethod (m.method.GetGenericArguments().Map (m.genericArgumentsMap).ToArray()) : m.method).SingleOrDefault(); 
    } 

} 

Так дали

public class Foos { 
    public void Foo<T1 , T2 , T3> (int a , T1 b , IEnumerable<T2> c , Expression<Func<T1 , T3 , string>> d) { 
    } 
    public void Foo<T1 , T2 , T3> (int a , T1 b , IEnumerable<T2> c , Expression<Func<T1 , T3 , int>> d) { 
    } 
    public void Foo() { 
    } 
    public void Foo (string s) { 
    } 
} 

Я могу выбрать метод, который я хочу:

var method = typeof (Foos).MakeGenericMethod ("Foo" , new[] { typeof (int) , typeof (DateTime) , typeof (IEnumerable<string>) , typeof (Expression<Func<DateTime , double , int>>) }); 
method.Invoke (new Foos() , new object[] { 1 , DateTime.Now , null , null }); 

var method = typeof (Foos).MakeGenericMethod ("Foo" , Type.EmptyTypes); 
method.Invoke (new Foos() , new object[] { }); 

var method = typeof (Foos).MakeGenericMethod ("Foo" , new[] { typeof (string) }); 
method.Invoke (new Foos() , new object[] { "zozo" }); 

Это, кажется, поддерживают, не общие методы, но не поддерживает явные общие аргументы (очевидно,), и, возможно, нужна какая-то работа, но в этом суть.

3

Да, вы должны в основном просмотреть все методы. Там нет никакого способа получения прямо в общий метод, просто указав количество параметров типа и т.д.

Однако одна вещь, которую вы может использовать тот факт, что перегружает с той же подписью, кроме типичности перегружаются только самые номер параметров типового типа. Вы не можете использовать ограничения или имена параметров типа, чтобы иметь:

void Foo<T1>(String x) 
void Foo<T2>(String y) 

т.д.

Да, все это довольно сложно. Я бы очень старался избежать необходимости делать это, если бы был вами.

1

Вызов универсальных методов с отражением действительно может быть таким уродливым. Чтобы устранить правильное значение MethodInfo, я обычно стараюсь выбрать самый простой уникальный фактор и работать с ним - что может означать количество общих параметров/параметров метода и т. Д. Я стараюсь избегать сравнения самих несвязанных общих типов.

dynamic С в 4.0, я считаю, (хотя я должен был бы проверить), что это может сделать многие из этих вызовов проще (так как она по существу делает метод/родовое разрешение во время выполнения), но это не делает работают методы расширения ;-(

Другим вариантом является Expression, который может быть немного симпатичнее в некоторых отношениях, но вы должны Compile() его и т.д., и Expression является самого сложно (на самом деле, забыть об этом. - это все еще сложно)

0

Когда вы вызываете GetMethod или InvokeMember в экземпляре типа, вы можете передать пользовательский подкласс класса Binder. Пользовательское связующее может изменить способ выбора участников, так как они получают список кандидатов-кандидатов для выбора.

Смежные вопросы