GetMethod для общего метода

Я пытаюсь получить метод MethodInfo для Where для типа Enumerable:

typeof (Enumerable).GetMethod("Where", new Type[] { typeof(IEnumerable), typeof(Func) }) 

но получить null. Что я делаю не так?

Однако этот предыдущий ответ работает в некоторых случаях:

  • Он не обрабатывает вложенные общие типы, такие как тип параметра Action> . Он будет обрабатывать все Action<> качестве совпадений, например string.Concat(IEnumerable) и string.Concat(IEnumerable) будет совпадать, если поиск "Concat" с типом IEnumerable<> on тип строки. То, что действительно желательно, рекурсивно обрабатывает вложенные генерические типы, обрабатывая все общие параметры как совпадающие друг с другом независимо от имени, а НЕ соответствующие конкретным типам.
  • Он возвращает первый метод, а не исключает исключение, если результат неоднозначен, например type.GetMethod() . Таким образом, вы можете получить метод, который вам нужен, если вам повезет, или вы не можете.
  • Иногда необходимо указать BindingFlags , чтобы избежать двусмысленности, например, когда метод производного classа «скрывает» метод базового classа. Обычно вы хотите найти методы базового classа, но не в специализированном случае, когда вы знаете, какой метод вы ищете, в производном classе. Или вы, возможно, знаете, что ищете статический метод экземпляра vs, public vs private и т. Д. И не хотите совпадать, если он не является точным.
  • Он не адресует другую серьезную ошибку с type.GetMethods() , поскольку он также не ищет базовые интерфейсы для методов при поиске метода по типу интерфейса. Хорошо, может быть, это придирчиво, но это еще один серьезный недостаток в GetMethods() , который был для меня проблемой.
  • type.GetMethods() неэффективен, type.GetMember(name, MemberTypes.Method, ...) возвращает только методы с совпадающим именем вместо ВСЕХ методов в типе.
  • Как конечный nit-pick, имя GetGenericMethod() может вводить в заблуждение, поскольку вы можете попытаться найти не-общий метод, который имеет параметр типа где-то в типе параметра из-за типичного типа объявления.

Вот версия, которая затрагивает все эти вещи и может использоваться как замена общего назначения для ошибочного GetMethod() . Обратите внимание, что предоставляются два метода расширения: один с BindingFlags и один без (для удобства).

 ///  /// Search for a method by name and parameter types. /// Unlike GetMethod(), does 'loose' matching on generic /// parameter types, and searches base interfaces. ///  ///  public static MethodInfo GetMethodExt( this Type thisType, string name, params Type[] parameterTypes) { return GetMethodExt(thisType, name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, parameterTypes); } ///  /// Search for a method by name, parameter types, and binding flags. /// Unlike GetMethod(), does 'loose' matching on generic /// parameter types, and searches base interfaces. ///  ///  public static MethodInfo GetMethodExt( this Type thisType, string name, BindingFlags bindingFlags, params Type[] parameterTypes) { MethodInfo matchingMethod = null; // Check all methods with the specified name, including in base classes GetMethodExt(ref matchingMethod, thisType, name, bindingFlags, parameterTypes); // If we're searching an interface, we have to manually search base interfaces if (matchingMethod == null && thisType.IsInterface) { foreach (Type interfaceType in thisType.GetInterfaces()) GetMethodExt(ref matchingMethod, interfaceType, name, bindingFlags, parameterTypes); } return matchingMethod; } private static void GetMethodExt( ref MethodInfo matchingMethod, Type type, string name, BindingFlags bindingFlags, params Type[] parameterTypes) { // Check all methods with the specified name, including in base classes foreach (MethodInfo methodInfo in type.GetMember(name, MemberTypes.Method, bindingFlags)) { // Check that the parameter counts and types match, // with 'loose' matching on generic parameters ParameterInfo[] parameterInfos = methodInfo.GetParameters(); if (parameterInfos.Length == parameterTypes.Length) { int i = 0; for (; i < parameterInfos.Length; ++i) { if (!parameterInfos[i].ParameterType .IsSimilarType(parameterTypes[i])) break; } if (i == parameterInfos.Length) { if (matchingMethod == null) matchingMethod = methodInfo; else throw new AmbiguousMatchException( "More than one matching method found!"); } } } } ///  /// Special type used to match any generic parameter type in GetMethodExt(). ///  public class T { } ///  /// Determines if the two types are either identical, or are both generic /// parameters or generic types with generic parameters in the same /// locations (generic parameters match any other generic paramter, /// but NOT concrete types). ///  private static bool IsSimilarType(this Type thisType, Type type) { // Ignore any 'ref' types if (thisType.IsByRef) thisType = thisType.GetElementType(); if (type.IsByRef) type = type.GetElementType(); // Handle array types if (thisType.IsArray && type.IsArray) return thisType.GetElementType().IsSimilarType(type.GetElementType()); // If the types are identical, or they're both generic parameters // or the special 'T' type, treat as a match if (thisType == type || ((thisType.IsGenericParameter || thisType == typeof(T)) && (type.IsGenericParameter || type == typeof(T)))) return true; // Handle any generic arguments if (thisType.IsGenericType && type.IsGenericType) { Type[] thisArguments = thisType.GetGenericArguments(); Type[] arguments = type.GetGenericArguments(); if (thisArguments.Length == arguments.Length) { for (int i = 0; i < thisArguments.Length; ++i) { if (!thisArguments[i].IsSimilarType(arguments[i])) return false; } return true; } } return false; } 

Обратите внимание, что метод расширения IsSimilarType(Type) может быть опубликован и может быть полезен сам по себе. Я знаю, это имя не очень велико - вы можете придумать лучший, но может быть очень долго объяснять, что он делает. Кроме того, я добавил еще одно усовершенствование, проверив «ref» и типы массивов (refs игнорируются для сопоставления, но размеры массивов должны совпадать).

Итак, так Microsoft должна была это сделать. Это действительно не так сложно.

Да, я знаю, вы можете сократить некоторые из этой логики с помощью Linq, но я не являюсь большим поклонником Linq в низкоуровневых подпрограммах, подобных этому, и также, если Linq не будет так же легко следовать как исходный код, что часто не имеет места, ИМО.

Если вы любите Linq, и вы должны, вы можете заменить внутреннюю часть IsSimilarType() на это (превращает 8 строк в 1):

 if (thisArguments.Length == arguments.Length) return !thisArguments.Where((t, i) => !t.IsSimilarType(arguments[i])).Any(); 

Последнее: если вы ищете общий метод с общим параметром, например Method(T, T[]) , вам нужно будет найти Type, который является общим параметром ( IsGenericParameter == true ) для перехода к типу параметра (любой будет делать из-за соответствия подстановочных знаков). Однако вы не можете просто создать new Type() - вам нужно найти реальный (или создать один с TypeBuilder). Чтобы это стало проще, я добавил объявление public class T и добавил логику в IsSimilarType() чтобы проверить его и сопоставить любой общий параметр. Если вам нужен T[] , просто используйте T.MakeArrayType(1) .

К сожалению, дженерики не очень хорошо поддерживаются в .NET Reflection. В этом конкретном случае вам нужно вызвать GetMethods, а затем отфильтровать набор результатов для метода, который вы ищете. Метод расширения, такой как следующий, должен делать трюк.

 public static class TypeExtensions { private class SimpleTypeComparer : IEqualityComparer { public bool Equals(Type x, Type y) { return x.Assembly == y.Assembly && x.Namespace == y.Namespace && x.Name == y.Name; } public int GetHashCode(Type obj) { throw new NotImplementedException(); } } public static MethodInfo GetGenericMethod(this Type type, string name, Type[] parameterTypes) { var methods = type.GetMethods(); foreach (var method in methods.Where(m => m.Name == name)) { var methodParameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(); if (methodParameterTypes.SequenceEqual(parameterTypes, new SimpleTypeComparer())) { return method; } } return null; } } 

С этим в руке будет работать следующий код:

 typeof(Enumerable).GetGenericMethod("Where", new Type[] { typeof(IEnumerable<>), typeof(Func<,>) }); 
  • Есть ли способ получить псевдоним типа через reflection?
  • Как я могу динамически добавлять поле в class в C #
  • Как я могу оценивать выражение C # динамически?
  • Получить строковое имя свойства, используя reflection
  • Использовать reflection для вызова переопределенного базового метода
  • Как скопировать значение из classа X в class Y с тем же именем свойства в c #?
  • Есть что-то вроде instanceOf (class C) в Java?
  • Как получить имена параметров метода в Java 8 с использованием отражения?
  • Можете ли вы использовать reflection, чтобы найти имя текущего исполняемого метода?
  • могу ли я отразить типичный тип в java?
  • Получение типа classа из строки
  • Interesting Posts

    ObservableCollection и резьба

    Лучший способ перечислить файлы на Java, отсортированные по дате Модифицированные?

    Что противоположно GROUP_CONCAT в MySQL?

    увеличить zip_iterator и std :: sort

    Соединение не может быть выполнено, потому что целевая машина активно отказалась от него 127.0.0.1:3446

    Является ли CSS3 официальным стандартом?

    Гармоническая графика в текстовом режиме (только для предварительной загрузки / BIOS), а не в Windows

    Как я могу использовать файлы cshtml с Durandal?

    Photoshop CS4, мой инструмент линии теперь рисует стрелки, как мне вернуть значение по умолчанию и что это такое?

    Как я могу выяснить, какой процесс постоянно обращается к моему жесткому диску в Windows XP?

    Объекты ORM и сущности домена в Entity Framework 6.0

    Cygwin cygdrive и Windows Command Prompt

    Как настроить уведомление с помощью настраиваемого звука в android

    Альтернатива для «tail -f», которая следует за именем файла

    Папка всегда становится доступной только для чтения. Даже когда я меняю его

    Давайте будем гением компьютера.