确定两个不同的属性Info是否来自同一接口



我一直在使用

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具有DeclaringType == typeof(IAnimal),而
  • Dog.IsHungryDeclaringType == typeof(Animal)

我想不出一种简单的方法来比较界面和具体类型的属性表达式,而不要求助于简单的Name比较(这很容易发生误报) - 但我无法想到任何没有的东西涉及列举两种类型所继承的所有接口,并寻找两套属性名称的任何东西。


Q:我们可以创建一个函数,该函数在比较上述4个属性表达式产生的任何属性时返回true。(例如,检测它们都表示/代表/实现相同的基本接口属性吗?)

,如果使用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和vice-vices-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice-vice》中仍然可以明确地实现并用具有相同名称的新属性隐藏其属性,因此额外的检查不会使其任何"更安全"。

不确定我是否可以告诉一个PropertyInfo实例是否代表另一个的接口实现。

我想出的解决方案将getter/setter MethodInfoInterfaceMapping进行了比较。它通过了我能想到的所有测试,但我并不是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();
}

相关内容

最新更新