Как определить, является ли метод универсальным экземпляром универсального метода - PullRequest
7 голосов
/ 23 июля 2010

Мне передали MethodInfo в функцию, и я хочу сделать следующее

MethodInfo containsMethod = typeof(ICollection<>).GetMethod("Contains");
if (methodInfo.Equals(containsMethod)
{
   // do something
}

Но это не работает, потому что methodInfo имеет определенный универсальный тип. Для примера действительно работает, если бы я знал, что ICollection всегда имел тип string.

MethodInfo containsMethod = typeof(ICollection<string>).GetMethod("Contains");
if (methodInfo.Equals(containsMethod)
{
   // do something
}

Как я могу проверить, является ли MethodInfo ЛЮБЫМ типизированным экземпляром универсального метода, не заботясь о том, что это за тип?

Спасибо.

РЕДАКТИРОВАТЬ: разъяснение вопроса

Как правильно указано, метод не является универсальным, но содержащий класс является таким, поэтому вопрос состоит в том, как узнать, предназначен ли MethodInfo для типа, который является типизированным экземпляром ICollection <>.

РЕДАКТИРОВАТЬ: больше контекста

Я пишу провайдеру Linq и пытаюсь обработать случай "in"

IList<string> myList = new List<string>{ "1", "2" };

from Something s in ...
where myList.Contains(s.name)
select s;

Ответы [ 5 ]

4 голосов
/ 23 июля 2010

Обратите внимание, что ICollection<T>.Contains является , а не универсальным методом - это неуниверсальный метод универсального типа.В противном случае IsGenericMethod и GetGenericTypeDefinition помогут.Вы можете получить определение общего типа (DeclaringType.GetGenericTypeDefinition()) и вернуться к Contains, но мне интересно, если вы подходите к этой проблеме трудным путем.

Обычно, если вы используете отражение, это можетпрагматично переходить к неуниверсальному IList - если только не требуется данные типа (например, для метапрограммирования).И в этом случае я хотел бы рассмотреть вопрос о том, можете ли вы упростить настройку здесь.

3 голосов
/ 23 июля 2010

Вы можете проверить тип объявления:

if( methodInfo.Name == "Contains" 
    &&  methodInfo.DeclaringType.IsGenericType
    && methodInfo.DeclaringType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
2 голосов
/ 23 июля 2010

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

static bool IsContainsMethod(MethodInfo methodInfo)
{
    Type[] types = { methodInfo.GetParameters().First().ParameterType };
    MethodInfo containsMethod = typeof(ICollection<>).MakeGenericType(types).GetMethod("Contains");
    return methodInfo.Equals(containsMethod);
}
0 голосов
/ 23 июля 2010

попробуйте этот метод

    public static bool CheckGenericMethod(MethodInfo methodInfo)
    {
        bool areSimilarMethods = false;
        MethodInfo methodToCompare = typeof(ISomeInterface<>).GetMethod("func");
        Type interfaceInfo = methodInfo.DeclaringType.GetInterface(methodToCompare.DeclaringType.FullName);

        if (interfaceInfo != null)
            areSimilarMethods = (methodToCompare.Name.Equals(methodInfo.Name)
            && interfaceInfo.FullName.Contains(methodToCompare.DeclaringType.FullName));
        else
        {
            areSimilarMethods = methodToCompare.DeclaringType.Equals(methodInfo.DeclaringType);
        }

        return areSimilarMethods;

    }

и вот полный пример использования.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace TestReflection
{
    public class Program
    {
        static void Main(string[] args)
        {
            MethodInfo info1 = typeof(ISomeInterface<>).GetMethod("func");
            MethodInfo info2 = typeof(MyStringCollection).GetMethod("func");
            MethodInfo info3 = typeof(MyProgramCollection).GetMethod("func");
            MethodInfo info4 = typeof(MyXCollection).GetMethod("func");

            if (CheckGenericMethod(info1)) Console.WriteLine("true");else Console.WriteLine("false");
            if (CheckGenericMethod(info2)) Console.WriteLine("true");else Console.WriteLine("false");
            if (CheckGenericMethod(info3)) Console.WriteLine("true");else Console.WriteLine("false");
            if (CheckGenericMethod(info4)) Console.WriteLine("true"); else Console.WriteLine("false");

            Console.ReadKey();
        }


        public static bool CheckGenericMethod(MethodInfo methodInfo)
        {
            bool areSimilarMethods = false;
            MethodInfo methodToCompare = typeof(ISomeInterface<>).GetMethod("func");
            Type interfaceInfo = methodInfo.DeclaringType.GetInterface(methodToCompare.DeclaringType.FullName);

            if (interfaceInfo != null)
                areSimilarMethods = (methodToCompare.Name.Equals(methodInfo.Name)
                && interfaceInfo.FullName.Contains(methodToCompare.DeclaringType.FullName));
            else
            {
                areSimilarMethods = methodToCompare.DeclaringType.Equals(methodInfo.DeclaringType);
            }

            return areSimilarMethods;

        }
    }

    public interface ISomeInterface<T> where T : class
    {
        T func(T s);
    }

    public class MyStringCollection : ISomeInterface<string>
    {
        public string func(string s)
        {
            return s;
        }
    }

    public class MyProgramCollection : ISomeInterface<Program>
    {
        public Program func(Program s)
        {
            return s;
        }
    }

    public class MyXCollection
    {
        public int func(int s)
        {
            return s;
        }
    }

}
0 голосов
/ 23 июля 2010

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

MethodInfo containsMethod = typeof(ICollection<>).GetMethod("Contains");
var methodIndex = Array.IndexOf(methodInfo.DeclaringType.GetMethods(), methodInfo);
var methodOnTypeDefinition = methodInfo.DeclaringType.GetGenericTypeDefinition().GetMethods()[methodIndex];
if (methodOnTypeDefinition.Equals(containsMethod))
{
    // do something
}
...