Использование отражения C # для поиска интерфейса, объявляющего функцию - PullRequest
0 голосов
/ 30 апреля 2018

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

public interface Ix
{
    void Function();
}
public class X : Ix
{
    public void Function() {}
}

public class Y : X
{
}

Информация о методе для класса X не содержит информации об объявлении функции в интерфейсе Ix. Вот звонок:

var info = typeof(X).GetMethod("Function");
var baseInfo = info.GetBaseDefinition()

Возвращает следующие данные:

info.DeclaringType --> MyNamespace.X
info.ReflectedType --> MyNamespace.X
baseInfo.DeclaringType --> MyNamespace.X
baseInfo.ReflectedType --> MyNamespace.X

Та же информация возвращается для класса Y.

Как я могу выяснить, что эта функция была объявлена ​​в интерфейсе Ix, не пройдя все реализованные интерфейсы и базовые классы класса X или Y? Я мог упустить что-то простое, но я не могу понять, что. Может ли это быть ошибкой?

Это .Net Core SDK версии 2.1.104 и Visual Studio 2017

Ответы [ 2 ]

0 голосов
/ 01 мая 2018

Вот метод расширения для извлечения Type интерфейсов, которые конкретный MethodInfo реализует в своем объявлении Type:

Во-первых, вспомогательный метод для получения всех карт интерфейса для Type:

public static IEnumerable<InterfaceMapping> GetAllInterfaceMaps(this Type aType) =>
    aType.GetTypeInfo()
         .ImplementedInterfaces
         .Select(ii => aType.GetInterfaceMap(ii));

Затем метод расширения, который использует помощник для получения интерфейсов для одного метода:

public static Type[] GetInterfacesForMethod(this MethodInfo mi) =>
    mi.ReflectedType
      .GetAllInterfaceMaps()
      .Where(im => im.TargetMethods.Any(tm => tm == mi))
      .Select(im => im.InterfaceType)
      .ToArray();

Если вам нужны интерфейсы для всех методов класса, вы можете использовать это вместо:

public static ILookup<MethodInfo, Type> GetMethodsForInterfaces(this Type aType) =>
    aType.GetAllInterfaceMaps()
         .SelectMany(im => im.TargetMethods.Select(tm => new { im.TargetType, im.InterfaceType, tm }))
         .ToLookup(imtm => imtm.tm, imtm => imtm.InterfaceType);

Чтобы получить объявления интерфейса, соответствующие методу, вы можете использовать это:

public static IEnumerable<MethodInfo> GetInterfaceDeclarationsForMethod(this MethodInfo mi) =>
    mi.ReflectedType
      .GetAllInterfaceMaps()
      .SelectMany(map => Enumerable.Range(0, map.TargetMethods.Length)
                                   .Where(n => map.TargetMethods[n] == mi)
                                   .Select(n => map.InterfaceMethods[n]));
0 голосов
/ 30 апреля 2018

Похоже, вам нужен обратный поиск информации, которую можно получить из GetInterfaceMap. Построить это довольно просто,

public static Dictionary<MethodInfo, List<(Type, MethodInfo)>> GetReverseInterfaceMap(Type t)
{
    var reverseMap = new Dictionary<MethodInfo, List<(Type, MethodInfo)>>();
    var maps = t.GetInterfaces().ToDictionary(i => i, i => t.GetInterfaceMap(i));
    foreach (var m in t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
    {
        var list = new List<(Type, MethodInfo)>();
        foreach (var (i, map) in maps)
        {
            for (int index = 0; index < map.TargetMethods.Length; index++)
            {
                var targetMethod = map.TargetMethods[index];
                if (targetMethod == m)
                {
                    list.Add((map.InterfaceType, map.InterfaceMethods[index]));
                    break;
                }
            }
        }

        reverseMap[m] = list;
    }

    return reverseMap;
}

Необходимо сделать несколько замечаний о том, как работают интерфейсы, потому что картина не так проста, как при использовании Derived -> Base -> Interface. Существует отображение, которое описывает конкретный метод, который вызывает каждый интерфейсный метод Эта информация легко доступна, потому что среда выполнения нуждается в ней для реализации диспетчеризации интерфейса. Однако обратное не так просто. Одним конкретным методом может быть отображение для нескольких интерфейсов. Также возможно, чтобы методы интерфейса были переназначены производными классами . Это все нужно учитывать.

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