这是.Net反射中的错误吗?

Mig*_*elo 5 .net c# reflection linq-expressions

答案是:不,这不是一个错误.不同之处在于ReflectedType.

所以真正的问题是:有没有一种方法可以比较两个PropertyInfo对象,对于同一个属性,但是从不同的类型反映出来,以便它返回true

原始问题

此代码通过使用两种不同的方式PropertyInfo同一属性生成两个对象.它来了,这些属性信息在某种程度上有不同的比较.我已经失去了一些时间试图弄清楚这一点.

我究竟做错了什么?

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace TestReflectionError
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.BufferWidth = 200;
            Console.WindowWidth = 200;

            Expression<Func<object>> expr = () => ((ClassA)null).ValueA;
            PropertyInfo pi1 = (((expr as LambdaExpression)
                .Body as UnaryExpression)
                .Operand as MemberExpression)
                .Member as PropertyInfo;

            PropertyInfo pi2 = typeof(ClassB).GetProperties()
                .Where(x => x.Name == "ValueA").Single();

            Console.WriteLine("{0}, {1}, {2}, {3}, {4}", pi1, pi1.DeclaringType, pi1.MemberType, pi1.MetadataToken, pi1.Module);
            Console.WriteLine("{0}, {1}, {2}, {3}, {4}", pi2, pi2.DeclaringType, pi2.MemberType, pi2.MetadataToken, pi2.Module);

            // these two comparisons FAIL
            Console.WriteLine("pi1 == pi2: {0}", pi1 == pi2);
            Console.WriteLine("pi1.Equals(pi2): {0}", pi1.Equals(pi2));

            // this comparison passes
            Console.WriteLine("pi1.DeclaringType == pi2.DeclaringType: {0}", pi1.DeclaringType == pi2.DeclaringType);
            Console.ReadKey();
        }
    }

    class ClassA
    {
        public int ValueA { get; set; }
    }

    class ClassB : ClassA
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的输出是:

Int32 ValueA, TestReflectionError.ClassA, Property, 385875969, TestReflectionError.exe
Int32 ValueA, TestReflectionError.ClassA, Property, 385875969, TestReflectionError.exe
pi1 == pi2: False
pi1.Equals(pi2): False
pi1.DeclaringType == pi2.DeclaringType: True
Run Code Online (Sandbox Code Playgroud)


罪魁祸首: PropertyInfo.ReflectedType

我发现这两个对象之间存在差异......它在ReflectedType.文档说明了这一点:

获取用于获取此成员的类对象.

Jef*_*ado 4

永远不要假设库中存在错误,除非您确实知道自己在做什么并且已经彻底测试了该问题。

PropertyInfo对象没有平等的概念。当然,它们可能代表相同的结果,但它们不会==使运算符重载,因此您不能假设它们应该重载。因为它们不这样做,所以它只是简单地进行引用比较,猜猜看,它们引用了两个单独的对象,因此是!=.

另一方面,Type对象也不会重载运算==符,但似乎将两个实例与==运算符进行比较是可行的。为什么?因为类型实例实际上是作为单例实现的,这是一个实现细节。因此,给定对同一类型的两个引用,它们将按预期进行比较,因为您实际上是在比较对同一实例的引用。

不要期望调用框架方法时获得的每个对象都会以相同的方式工作。框架中没有太多使用单例的内容。在此之前,请检查所有相关文档和其他来源。


重新审视这一点,我获悉从 .NET 4 开始,该类型已实现了Equals()方法和运算符。==不幸的是,文档并没有过多解释它们的行为,但使用 .NET Reflector 等工具可以揭示一些有趣的信息。

根据reflector,mscorlib程序集中的方法的实现如下:

[__DynamicallyInvokable]
public override bool Equals(object obj)
{
    return base.Equals(obj);
}

[__DynamicallyInvokable]
public static bool operator ==(PropertyInfo left, PropertyInfo right)
{
    return (object.ReferenceEquals(left, right)
        || ((((left != null) && (right != null)) &&
             (!(left is RuntimePropertyInfo) && !(right is RuntimePropertyInfo)))
        && left.Equals(right)));
}
Run Code Online (Sandbox Code Playgroud)

在继承链上上下移动(RuntimePropertyInfo-> PropertyInfo-> MemberInfo-> Object),Equals()一直调用基本实现,Object因此它实际上进行了对象引用相等性比较。

操作==员专门检查以确保两个PropertyInfo对象都不是RuntimePropertyInfo对象。据我所知,PropertyInfo使用反射(在此处显示的用例中)获得的每个对象都将返回一个RuntimePropertyInfo.

基于此,看起来框架设计者认真地使其(运行时)PropertyInfo对象不可比较,即使它们代表相同的属性。您只能检查属性是否引用同一个PropertyInfo实例。我无法告诉你为什么他们做出这个决定(我有我的理论),你必须从他们那里听到。