如何可靠地比较两个PropertyInfos或方法?

Rob*_*ton 12 c# reflection

方法也是如此:

我得到了两个PropertyInfo实例或者从它们所在的类GetProperty()或者GetMember()等等中提取的方法(或者可能来自MemberExpression).

我想确定它们是否实际上指的是相同的属性或相同的方法

(propertyOne == propertyTwo)
Run Code Online (Sandbox Code Playgroud)

要么

(methodOne == methodTwo)
Run Code Online (Sandbox Code Playgroud)

显然,这不会真正起作用,您可能正在查看相同的属性,但它可能是从类层次结构的不同级别(通常在这种情况下propertyOne != propertyTwo)中提取的

当然,我可以查看DeclaringType,并重新请求该属性,但是当你开始考虑时,这开始变得有点混乱

  • 在接口上声明并在类上实现的属性/方法
  • 在基类(虚拟)上声明的属性/方法,并在派生类上重写
  • 在基类上声明的属性/方法,用'new'覆盖(在IL世界中,这不是什么特别的iirc)

在一天结束时,我只是想能够在两个属性或两个方法之间进行智能相等检查,我80%确定上述要点并未涵盖所有边缘情况,而我我可以坐下来,写一堆测试并开始玩,我很清楚我对这些概念实际实现方式的低级知识并不是很好,我希望这是一个已经回答的主题,我只是吮吸搜索.

最好的答案会给我一些实现上述目标的方法,解释已经处理了哪些边缘情况以及为什么:-)


澄清:

从字面上看,我想确保它们是同一个属性,这里有一些例子

public interface IFoo
{
     string Bar { get; set; }
}

public class Foo : IFoo
{
     string Bar { get; set; }
}

typeof(IFoo).GetProperty("Bar")
Run Code Online (Sandbox Code Playgroud)

typeof(Foo).GetProperty("Bar")
Run Code Online (Sandbox Code Playgroud)

将返回两个不相等的属性信息:

public class BaseClass
{
     public string SomeProperty { get; set ; }
}

public class DerivedClass : BaseClass { }


typeof(BaseClass).GetMethod("SomeProperty")
Run Code Online (Sandbox Code Playgroud)

typeof(DerivedClass).GetProperty("SomeProperty")
Run Code Online (Sandbox Code Playgroud)

我实际上无法记住这两个现在是否会返回相同的对象,但在我的世界中它们是平等的.

同理:

public class BaseClass
{
    public virtual SomeMethod() { }
}

public class DerivedClass
{
    public override SomeMethod() { }
}

typeof(BaseClass).GetMethod("SomeMethod")
Run Code Online (Sandbox Code Playgroud)

typeof(DerivedClass).GetProperty("SomeMethod")
Run Code Online (Sandbox Code Playgroud)

再次,这些不匹配 - 但我希望它们(我知道它们不是特别相同,但在我的域中它们是因为它们引用相同的原始属性)

我可以在结构上做到这一点,但这将是"错误的".

进一步说明:

你怎么甚至要求隐藏另一处房产的房产呢?似乎我之前的一个假设是无效的,默认情况下默认实现GetProperty("name")将引用当前级别.

BindingFlags.DeclaringType 似乎只是为了最终返回null!

Jon*_*Jon 3

查看PropertyInfoIFoo/Foo示例中的对象,我们可以得出以下结论:

  1. 没有直接的方法可以查看该属性最初是在哪个类/接口上声明的。
  2. 因此,要检查该属性是否确实在祖先类上声明,我们需要迭代祖先并查看该属性是否也存在于它们上。
  3. 接口也是如此,我们需要Type.GetInterfaces从那里调用和工作。不要忘记接口可以实现其他接口,因此这必须是递归的。

那么让我们来尝试一下吧。首先,覆盖继承的属性:

PropertyInfo GetRootProperty(PropertyInfo pi)
{
    var type = pi.DeclaringType;

    while (true) {
        type = type.BaseType;

        if (type == null) {
            return pi;
        }

        var flags = BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance |
                    BindingFlags.Public | BindingFlags.Static;
        var inheritedProperty = type.GetProperty(pi.Name, flags);

        if (inheritedProperty == null) {
            return pi;
        }

        pi = inheritedProperty;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,涵盖接口中声明的属性(使用 DFS 搜索):

PropertyInfo GetImplementedProperty(PropertyInfo pi)
{
    var type = pi.DeclaringType;
    var interfaces = type.GetInterfaces();

    if (interfaces.Length == 0) {
        return pi;
    }

    var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;
    var query = from iface in interfaces
                let implementedProperty = iface.GetProperty(pi.Name, flags)
                where implementedProperty != pi
                select implementedProperty;

    return query.DefaultIfEmpty(pi).First();
}
Run Code Online (Sandbox Code Playgroud)

将这些结合在一起:

PropertyInfo GetSourceProperty(PropertyInfo pi)
{
    var inherited = this.GetRootProperty(pi);
    if (inherited != pi) {
        return inherited;
    }

    var implemented = this.GetImplementedProperty(pi);
    if (implemented != pi) {
        return implemented;
    }

    return pi;
}
Run Code Online (Sandbox Code Playgroud)

这应该有效。它没有考虑具有相同名称但不同类型和/或数量的索引参数的索引属性,因此这留给读者作为众所周知的练习。

免责声明:我什至没有编译这个(现在没有时间运行测试)。它旨在作为“the”答案的起点,因为到目前为止还没有答案。