Как получить MethodInfo для Enumerable.SequenceEqual

Я пытаюсь получить MethodInfo для Enumerable.SequenceEqual, используя Type.GetMethod(...). До сих пор я пробовал следующее:

var mi = typeof(Enumerable).GetMethod(nameof(Enumerable.SequenceEqual),
 BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any,
 new Type[] { typeof(IEnumerable<>), typeof(IEnumerable<>) }, null);

и

var enumTyped = typeof(IEnumerable<>).MakeGenericType(ValueType);
var mi = typeof(Enumerable).GetMethod(nameof(Enumerable.SequenceEqual),
 BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any,
 new Type[] { enumTyped, enumTyped }, null);

Однако оба решения возвращают null вместо метода, который я хочу. Я знаю, что метод можно восстановить, вызвав GetMethods() и фильтрацию, но я очень хотел бы узнать, как его получить, используя GetMethod(...).

2 ответа

К сожалению, для получения общих генерических методов, использующих Type.GetMethod(string name, Type[] types), вам необходимо предоставить метод для правильных общих типов в Type[], что означает, что при попытке сделать это:

Type requiredType = typeof(IEnumerable<>);
typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { requiredType, requiredType });

Вам действительно нужно было сделать что-то вроде этого:

Type requiredType = typeof(IEnumerable<tsource>);
typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { requiredType, requiredType });
</tsource>

Так как если вы посмотрите на подпись SequenceEqual, общий тип IEnumerable not IEnumerable<>.

public static IEnumerable<tsource> SequenceEqual<tsource>(this IEnumerable<tsource> first, IEnumerable<tsource> second);
</tsource></tsource></tsource></tsource>

НО: у вас нет доступа к типу TSource, чтобы использовать его. Таким образом, единственный способ получить IEnumerable - использовать отражение, подобное следующему:

MethodInfo info = typeof(Enumerable)
 .GetMethods(BindingFlags.Static | BindingFlags.Public)
 .Where(x => x.Name.Contains("SequenceEqual"))
 .Single(x => x.GetParameters().Length == 2);
Type genericType = typeof(IEnumerable<>).MakeGenericType(infos.GetGenericArguments());

и получить метод, использующий

typeof(Enumerable).GetMethod("SequenceEqual", new Type[] { genericType, genericType });

Но это требует от нас метода SequenceEqual в любом случае, поэтому печальный факт дает методу общий метод, когда несколько перегрузок с использованием GetMethod вместо GetMethods практически невозможно * (Вы CAN реализуете Binder и используете его в методе GetMethod, но для этого потребуется очень длинное кодирование, которое, возможно, будет ошибкой и недостижимым, и его следует избегать).


Хотите добавить к предыдущим ответам немного. Во-первых, действительно невозможно использовать один единственный метод GetMethod, чтобы делать то, что вы хотите. Но, если вы не хотите вызывать GetMethods и получать все 180+ методов Enumerable, вы можете сделать это:

var mi = typeof(Enumerable).GetMember(nameof(Enumerable.SequenceEqual), MemberTypes.Method,
 BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod).OfType<methodinfo>().ToArray();
</methodinfo>

GetMember call вернет вам всего 2 перегрузки метода SequenceEqual, из которых вы можете выбрать один и сделать MakeGenericMethod, как показано в других ответах. Кроме того, в зависимости от вашей цели вы можете использовать выражения:

var source = Expression.Parameter(
 typeof(IEnumerable<string>), "source");
var target = Expression.Parameter(
 typeof(IEnumerable<string>), "target");
var callExp = Expression.Call(typeof(Enumerable), "SequenceEqual", new Type[] { typeof(string)},
 source, target); 
var lambda = Expression.Lambda<func<ienumerable<string>, IEnumerable<string>, bool>>(callExp, source, target).Compile();
var result = lambda(new[] { "1", "2", "3" }, new[] { "1", "2", "3" });
Debug.Assert(result);
</string></func<ienumerable<string></string></string>

licensed under cc by-sa 3.0 with attribution.