Определить, если два разных PropertyInfo происходят из одного и того же интерфейса?

2

Я использую

public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second) =>
     first.DeclaringType == second.DeclaringType && first.Name == second.Name;

Чтобы определить, соответствует ли отраженная информация о свойствах какому-либо свойству, я выбрал базовый класс.

Этот подход начал разваливаться, когда я пытаюсь ссылаться на свойства, определенные в интерфейсах.

Например, представьте следующий сценарий многоинтерфейсного наследования:

interface IAnimal : { bool IsHungry { get; } }
interface IDog : IAnimal { }

abstract class Animal : IAnimal { public bool IsHungry { get; set; } }
class Dog : Animal, IDog { }

Если я создаю выражения свойств, все перечисленные ниже действительны:

Expression<Func<object, bool>> propertyExpression;
propertyExpression = (IAnimal animal) => animal.IsHungry
propertyExpression = (Animal animal) => animal.IsHungry
propertyExpression = (IDog dog) => dog.IsHungry
propertyExpression = (Dog dog) => dog.IsHungry

Поскольку каждый из этих типов определяет или наследует свойство IsHungry, все эти выражения допустимы. Можно даже утверждать, что все они ссылаются на одно и то же свойство (хотя я могу оценить тонкие различия между интерфейсом и объявлением экземпляра).

Моя проблема в том, что я хочу каким-то способом программно определить, что все эти свойства "исходят" из общего интерфейса IAnimal и являются совместимыми. К сожалению, мой тест возвращает false потому что:

  • IDog.IsHungry имеет IDog.IsHungry DeclaringType == typeof(IAnimal) тогда как
  • Dog.IsHungry имеет Dog.IsHungry DeclaringType == typeof(Animal)

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


Вопрос: Можем ли мы создать функцию, которая возвращает true при сравнении любого из PropertyInfo, полученного из приведенных выше 4 выражений свойств. (например, определить, что все они представляют/реализуют одно и то же свойство базового интерфейса?)

Теги:
reflection
.net-4.5

2 ответа

0

Решение, которое я придумал, сравнивает метод получения/установки MethodInfo с InterfaceMapping. Он проходит все тесты, о которых я только мог подумать, но я не уверен на 100%, что не бывает странных угловых случаев, которых это не улавливает.

public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second)
{
    if (first.DeclaringType == second.DeclaringType && first.Name == second.Name)
        return true;

    bool firstIsSecond = second.DeclaringType.IsAssignableFrom(first.DeclaringType);
    bool secondIsFirst = first.DeclaringType.IsAssignableFrom(second.DeclaringType);

    if (firstIsSecond || secondIsFirst)
    {
        PropertyInfo baseProp = firstIsSecond ? second : first;
        PropertyInfo derivedProp = firstIsSecond ? first : second;

        MethodInfo baseMethod, implMethod;
        if (baseProp.GetMethod != null && derivedProp.GetMethod != null)
        {
            baseMethod = baseProp.GetMethod;
            implMethod = derivedProp.GetMethod;
        }
        else if (baseProp.SetMethod != null && derivedProp.SetMethod != null)
        {
            baseMethod = baseProp.SetMethod;
            implMethod = derivedProp.SetMethod;
        }
        else
        {
            return false;
        }
        // Is it somehow possible to create a situation where both get and set exist
        // and the set method to be an implementation while the get method is not?

        if (baseMethod.DeclaringType.IsInterface)
            return IsInterfaceImplementation(implMethod, baseMethod);
        else
            return IsOverride(implMethod, baseMethod);
    }
    return false;
}

private static bool IsInterfaceImplementation(MethodInfo implMethod, MethodInfo interfaceMethod)
{
    InterfaceMapping interfaceMap = implMethod.DeclaringType.GetInterfaceMap(interfaceMethod.DeclaringType);
    int index = Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod);
    // I don't think this can ever be the case?
    if (index == -1)
        return false;
    MethodInfo targetMethod = interfaceMap.TargetMethods[index];
    return implMethod == targetMethod || IsOverride(implMethod, targetMethod);
}

private static bool IsOverride(MethodInfo implMethod, MethodInfo baseMethod)
{
    return implMethod.GetBaseDefinition() == baseMethod.GetBaseDefinition();
}
0

Это, вероятно, приведет к ложным срабатываниям в случае использования new ключевого слова для скрытия унаследованных свойств, но:

public static bool IsSameAsProperty(PropertyInfo first, PropertyInfo second) =>
    first == second || // If default equals implementation returns true, no doubt
    first.Name == second.Name && (
        first.DeclaringType == second.DeclaringType ||
        first.DeclaringType.IsAssignableFrom(second.DeclaringType) ||
        second.DeclaringType.IsAssignableFrom(first.DeclaringType));

Я полагаю, что я мог бы быть немного более конкретным и проверить, если first.DeclaringType.IsInterface и наоборот, но все же можно явно реализовать этот интерфейс и скрыть его свойство с новым с тем же именем, поэтому дополнительная проверка не будет сделать это "безопаснее".

Не уверен, смогу ли я сказать, представляет ли один экземпляр PropertyInfo реализацию интерфейса другого.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню