这是.NET反映一个错误?这是、错误、NET

2023-09-04 09:53:38 作者:晗夏

答案是:不,这不是一个错误。 所不同的是在 ReflectedType 。

因此,这里真正的问题是:有没有比较两个的PropertyInfo 的对象,对于同一财产的一种方式,但不同类型的体现,所以它返回

原题

这code产生两个的PropertyInfo 对象的非常相同的属性,用两种不同的方法 。它配备的是,这些财产的相关信息比较不同不知。我已经失去了一些时间试图弄清楚这一点。

我是什么做错了吗?

 使用系统;
使用System.Linq的;
使用System.Linq.Ex pressions;
使用的System.Reflection;

命名空间TestReflectionError
{
    类节目
    {
        静态无效的主要(字串[] args)
        {
            Console.BufferWidth = 200;
            Console.WindowWidth = 200;

            防爆pression< Func键<对象>> EXPR =()=> ((ClassA的)空).ValueA;
            的PropertyInfo PI1 =(((表达式为LambdaEx pression)
                。体作为UnaryEx pression)
                .Operand为MemberEx pression)
                。成员为的PropertyInfo;

            的PropertyInfo PI2 = typeof运算(ClassB的).GetProperties()
                。凡(X => x.Name ==值a)单()。

            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);

            //这两个比较FAIL
            Console.WriteLine(PI1 == PI2:{0},PI1 == PI2);
            Console.WriteLine(pi1.Equals(PI2):{0},pi1.Equals(PI2));

            //这个比较传递
            Console.WriteLine(pi1.DeclaringType == pi2.DeclaringType:{0},pi1.DeclaringType == pi2.DeclaringType);
            Console.ReadKey();
        }
    }

    类ClassA的
    {
        公众诠释值a {获得;组; }
    }

    类ClassB的:ClassA的
    {
    }
}
 

下面的输出是:

 的Int32值a,TestReflectionError.ClassA,地产,385875969,TestReflectionError.exe
INT32值a,TestReflectionError.ClassA,地产,385875969,TestReflectionError.exe
PI1 == PI2:假
pi1.Equals(PI2):假
pi1.DeclaringType == pi2.DeclaringType:真
 
打开一些网站就会出现Network Error 什么问题

罪魁祸首: PropertyInfo.ReflectedType

我发现这两个对象之间的差异......这是在 ReflectedType 。该文件说这样的:

  

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

解决方案

永远不要的假设有一个在库中的缺陷,除非你真的知道自己在做什么,你已经彻底测试了问题。

的PropertyInfo 对象有平等的概念。当然,他们可能会重新present相同的结果,但他们的不的超载 == 运营商所以你不能假设他们的 的应该。因为他们不这样做,它只是简单地做一个参考比较,你猜怎么着,他们指的是两个不同的对象,因此!=

在另一方面,键入对象也做不超载 == 运营商,但它似乎比较两个实例与 == 运营商将工作。为什么?由于类型实例实际上实现为单身人士,这是一个实现细节。因此,考虑两个引用相同类型的,他们将比较按预期的方式,因为你实际上是比较引用同一个实例。

不要指望的每次的对象,你永远不会得到调用框架方法时,将工作方式相同。没有多少在使用单身的框架。检查才这样做的所有相关文件和其他来源。

重温这一点,我已经被告知,作为.NET 4中,等于()方法和 == 运营商已实施的类型。不幸的是,文件没有解释他们的行为很多,但使用的工具,如.net反射揭示了一些有趣的信息。

的PropertyInfo - > 的MemberInfo - > 对象),等于()调用基实现一路攀升至对象所以它实际上做了对象引用相等比较。

== 运营商特别检查,以确保没有的PropertyInfo 对象是 RuntimePropertyInfo 对象。而据我所知,每个的PropertyInfo 的对象,你会得到使用反射(在这里显示的用例)将返回一个 RuntimePropertyInfo

在此基础上

,它看起来像框架设计的认真的说得那么(运行)的PropertyInfo 对象不具有可比性,即使他们再present相同的属性。你可以只检查,看看属性指的是相同的PropertyInfo 实例。我不能告诉你为什么,他们已经做出了这个决定(我有我的理论),你必须听听来自他们。

ANSWER is: No, this is not a bug. The difference is in the ReflectedType.

So the real question here is: Is there a way of comparing two PropertyInfo objects, for the same property, but reflected from different types, so that it returns true?

Original question

This code produces two PropertyInfo objects for the very same property, by using two different ways. It comes that, these property infos compare differently somehow. I have lost some time trying to figure out this out.

What am I doing wrong?

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
    {
    }
}

The output here is:

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

Culprit: PropertyInfo.ReflectedType

I have found a difference between these two objects... it is in the ReflectedType. The documentation says this:

Gets the class object that was used to obtain this member.

解决方案

Never assume there's a bug in the library unless you actually know what you're doing and you have exhaustively tested the issue.

PropertyInfo objects have no notion of equality. Sure they may represent the same result but they do not overload the == operator so you cannot assume that they should. Since they don't, it's just simply doing a reference comparison and guess what, they are referring to two separate objects and are therefore !=.

On the other hand, Type objects also do no overload the == operator but it seems comparing two instances with the == operator will work. Why? Because type instances are actually implemented as singletons and this is an implementation detail. So given two references to the same type, they will compare as expected because you are actually comparing references to the same instance.

Do not expect that every object you will ever get when calling framework methods will work the same way. There isn't much in the framework that use singletons. Check all relevant documentation and other sources before doing so.

Revisiting this, I've been informed that as of .NET 4, the Equals() method and == operator has been implemented for the type. Unfortunately the documentation doesn't explain their behavior much but using tools like .NET Reflector reveals some interesting info.

According to reflector, the implementations of the methods in the mscorlib assembly are as follows:

[__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)));
}

Going up and down the inheritance chain (RuntimePropertyInfo -> PropertyInfo -> MemberInfo -> Object), Equals() calls the base implementation all the way up to Object so it in effect does a object reference equality comparison.

The == operator specifically checks to make sure that neither PropertyInfo object is a RuntimePropertyInfo object. And as far as I can tell, every PropertyInfo object you would get using reflection (in the use-cases shown here) will return a RuntimePropertyInfo.

Based on this, it looks like the framework designers conscientiously made it so (Runtime) PropertyInfo objects non-comparable, even if they represent the same property. You may only check to see if the properties refer to the same PropertyInfo instance. I can't tell you why they've made this decision (I have my theories), you'd have to hear it from them.