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

C# 这是.Net反射中的错误吗?,c#,.net,reflection,linq-expressions,C#,.net,Reflection,Linq Expressions,答案是:不,这不是一个bug。 区别在于不同的地方 所以这里真正的问题是:有没有一种方法可以比较两个属性info对象,对于相同的属性,但是从不同的类型反映出来,从而返回true 原始问题 此代码使用两种不同的方法为相同的属性生成两个PropertyInfo对象。结果是,这些属性信息在某种程度上进行了不同的比较。我花了一些时间试图弄明白这一点 我做错了什么 using System; using System.Linq; using System.Linq.Expressions; using S

答案是:不,这不是一个bug。 区别在于不同的地方

所以这里真正的问题是:有没有一种方法可以比较两个
属性info
对象,对于相同的属性,但是从不同的类型反映出来,从而返回
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
    {
    }
}

罪魁祸首: 我发现这两个物体之间有区别。。。它位于反射类型中。文件上说:

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

除非您确实知道自己在做什么,并且已经对问题进行了详尽的测试,否则不要假设库中存在错误

PropertyInfo
对象没有相等的概念。当然,它们可能表示相同的结果,但不会重载
==
运算符,因此您不能假设它们应该重载。因为它们没有,所以只需做一个引用比较,猜猜看是什么,它们引用的是两个独立的对象,因此是
=

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

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


再次讨论这个问题时,我被告知从.NET4开始,已经为该类型实现了
Equals()
方法和
==
运算符。不幸的是,文档并没有太多地解释它们的行为,但使用诸如.NETReflector之类的工具可以揭示一些有趣的信息

根据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)));
}
在继承链上下(
RuntimePropertyInfo
->
PropertyInfo
->
MemberInfo
->
Object
)中,
Equals()
调用基本实现,直到
Object
,因此它实际上执行对象引用相等比较

=
操作符专门检查以确保
PropertyInfo
对象都不是
RuntimePropertyInfo
对象。据我所知,使用反射(在这里显示的用例中)得到的每个
PropertyInfo
对象都将返回
RuntimePropertyInfo


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

你为什么不比较MetadataToken和Module呢

根据组合唯一标识的文档


一个值,它与模块一起唯一地标识元数据元素


我比较了
DeclaringType
Name
。这会报告来自两种不同泛型类型的“相同”属性是不同的(例如,
List.Count
List.Count
)。比较
MetadataToken
Module
会发现这两个属性是相同的。

一开始,如果两个
MemberInfo
在直接访问该成员(而不是通过反射)时返回相同的值,那么它们是相等的,这似乎是有意义的。对于
FieldInfo
而言,这似乎更合理。但是,对于
PropertyInfo
来说,它不是很清楚,因为属性可以在子类中扩展,不同的
CustomAttributes
可以添加到成员声明中。这意味着严格考虑访问值不足以定义相等。但是,如果这是您想要的等式的定义,那么您可能需要考虑<代码> AeaRealAl3(…)< /C> >方法:

private class Person {
    [CustomAttribute1]
    public virtual String Name { get; set; }
}

private class Person2 : Person {
    [CustomAttribute2]
    public override String Name { get; set; }
}

public static void TestMemberInfoEquality() {
    MemberInfo m1 = ExpressionEx.GetMemberInfo<Person>(p => p.Name);
    MemberInfo m2 = ExpressionEx.GetMemberInfo<Person2>(p => p.Name);
    bool b1 = m1.MetadataToken == m2.MetadataToken; // false
    bool b2 = m1 == m2; // false (because ReflectedType is different)
    bool b3 = m1.DeclaringType == m2.DeclaringType; // false
    bool b4 = AreEqual1(m1, m2); // false
    bool b5 = AreEqual2(m1, m2); // false
    bool b6 = AreEqual3(m1, m2); // true
}

public static bool AreEqual1(MemberInfo m1, MemberInfo m2) {
    return m1.MetadataToken == m2.MetadataToken && m1.Module == m2.Module;
}

public static bool AreEqual2(MemberInfo m1, MemberInfo m2) {
    return m1.DeclaringType == m2.DeclaringType && m1.Name == m2.Name;
}

public static bool AreEqual3(MemberInfo m1, MemberInfo m2) {
    return m1.GetRootDeclaration() == m2.GetRootDeclaration();
}

public static MemberInfo GetRootDeclaration(this MemberInfo mi) {
    Type ty = mi.ReflectedType;
    while (ty != null) {
        MemberInfo[] arr = ty.GetMember(mi.Name, mi.MemberType, BindingFlags.Instance | BindingFlags.Public);
        if (arr == null || arr.Length == 0)
            break;
        mi = arr[0];
        ty = ty.BaseType;
    }
    return mi;
}
私人阶级人士{
[自定义属性1]
公共虚拟字符串名称{get;set;}
}
私人类人员2:人员{
[客户属性2]
公共重写字符串名称{get;set;}
}
公共静态void TestMemberInfoEquality(){
MemberInfo m1=ExpressionEx.GetMemberInfo(p=>p.Name);
MemberInfo m2=ExpressionEx.GetMemberInfo(p=>p.Name);
bool b1=m1.MetadataToken==m2.MetadataToken;//false
bool b2=m1==m2;//false(因为ReflectedType不同)
bool b3=m1.DeclaringType==m2.DeclaringType;//false
boolb4=arequal1(m1,m2);//假
boolb5=arequal2(m1,m2);//假
布尔b6=arequal3(m1,m2);//真
}
公共静态bool arequal1(MemberInfo m1、MemberInfo m2){
返回m1.MetadataToken==m2.MetadataToken&&m1.Module==m2.Module;
}
公共静态bool arequal2(MemberInfo m1、MemberInfo m2){
返回m1.DeclaringType==m2.DeclaringType&&m1.Name==m2.Name;
}
公共静态bool arequal3(MemberInfo m1、MemberInfo m2){
返回m1.GetRootDeclaration()==m2.GetRootDeclaration();
}
公共静态MemberInfo GetRootDeclaration(此MemberInfo mi){
ty型=mi.反射型;
while(ty!=null){
MemberInfo[]arr=ty.GetMember(mi.Name,mi.Memb
static void Main(string[] args)
{
    Console.BufferWidth = 200;
    Console.WindowWidth = 140;

    PropertyInfo pi1 = typeof(ClassA).GetProperties()
        .Where(x => x.Name == "ValueA").Single();
    PropertyInfo pi2 = typeof(ClassB).GetProperties()
        .Where(x => x.Name == "ValueA").Single();
    PropertyInfo pi0 = typeof(ClassA).GetProperties()
        .Where(x => x.Name == "ValueB").Single();
    PropertyInfo pi3 = typeof(ClassB).GetProperties()
        .Where(x => x.Name == "ValueB").Single();
    PropertyInfo pi4 = typeof(ClassC).GetProperties()
        .Where(x => x.Name == "ValueA").Single();
    PropertyInfo pi5 = typeof(ClassC).GetProperties()
        .Where(x => x.Name == "ValueB").Single();


    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}", pi1, pi1.ReflectedType, pi1.DeclaringType, pi1.MemberType, pi1.MetadataToken, pi1.Module);
    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}", pi2, pi2.ReflectedType, pi2.DeclaringType, pi2.MemberType, pi2.MetadataToken, pi2.Module);
    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}", pi0, pi0.ReflectedType, pi0.DeclaringType, pi0.MemberType, pi0.MetadataToken, pi1.Module);
    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}", pi3, pi3.ReflectedType, pi3.DeclaringType, pi3.MemberType, pi3.MetadataToken, pi3.Module);
    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}", pi4, pi4.ReflectedType, pi4.DeclaringType, pi4.MemberType, pi4.MetadataToken, pi4.Module);
    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}", pi5, pi5.ReflectedType, pi5.DeclaringType, pi5.MemberType, pi5.MetadataToken, pi5.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);


    pi1 = typeof(ClassA).GetProperties()
        .Where(x => x.Name == "ValueB").Single();

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

    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}", pi1, pi1.ReflectedType, pi1.DeclaringType, pi1.MemberType, pi1.MetadataToken, pi1.Module);
    Console.WriteLine("{0}, {1}, {2}, {3}, {4}, {5}", pi2, pi2.ReflectedType, 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));


    Console.ReadKey();
}
class ClassA
{
    public int ValueA { get; set; }
    public int ValueB { get; set; }
}
class ClassB : ClassA
{
    public new int ValueB { get; set; } 
}
class ClassC
{
    public int ValueA { get; set; }
    public int ValueB { get; set; }
}
private class Person {
    [CustomAttribute1]
    public virtual String Name { get; set; }
}

private class Person2 : Person {
    [CustomAttribute2]
    public override String Name { get; set; }
}

public static void TestMemberInfoEquality() {
    MemberInfo m1 = ExpressionEx.GetMemberInfo<Person>(p => p.Name);
    MemberInfo m2 = ExpressionEx.GetMemberInfo<Person2>(p => p.Name);
    bool b1 = m1.MetadataToken == m2.MetadataToken; // false
    bool b2 = m1 == m2; // false (because ReflectedType is different)
    bool b3 = m1.DeclaringType == m2.DeclaringType; // false
    bool b4 = AreEqual1(m1, m2); // false
    bool b5 = AreEqual2(m1, m2); // false
    bool b6 = AreEqual3(m1, m2); // true
}

public static bool AreEqual1(MemberInfo m1, MemberInfo m2) {
    return m1.MetadataToken == m2.MetadataToken && m1.Module == m2.Module;
}

public static bool AreEqual2(MemberInfo m1, MemberInfo m2) {
    return m1.DeclaringType == m2.DeclaringType && m1.Name == m2.Name;
}

public static bool AreEqual3(MemberInfo m1, MemberInfo m2) {
    return m1.GetRootDeclaration() == m2.GetRootDeclaration();
}

public static MemberInfo GetRootDeclaration(this MemberInfo mi) {
    Type ty = mi.ReflectedType;
    while (ty != null) {
        MemberInfo[] arr = ty.GetMember(mi.Name, mi.MemberType, BindingFlags.Instance | BindingFlags.Public);
        if (arr == null || arr.Length == 0)
            break;
        mi = arr[0];
        ty = ty.BaseType;
    }
    return mi;
}