Is this a bug in .Net reflection?
答案是:不,这不是一个错误。不同之处在于反射类型。
所以这里真正的问题是:有没有一种方法可以比较两个
此代码通过使用两种不同的方法为同一属性生成两个
我做错什么了?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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 { } } |
这里的输出是:
1 2 3 4 5 | 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 |
< BR>
罪魁祸首:我发现这两个物体有区别…它在
Gets the class object that was used to obtain this member.
不要假设库中有一个bug,除非你真正知道自己在做什么,并且已经彻底测试了这个问题。
另一方面,
不要期望在调用框架方法时得到的每个对象都会以相同的方式工作。框架中使用单例的内容不多。检查所有相关文件和其他来源。
回顾这一点,我已经得知,从.NET 4开始,已经为该类型实现了
根据Reflector,mscorlib程序集中方法的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [__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))); } |
在继承链上下(
基于这一点,框架设计者似乎认真地使它(运行时)
我比较了
为什么不比较metadatatoken和module呢?
根据组合唯一标识的文档。
memberinfo.metadatatoken一个值,与模块结合,唯一地标识一个元数据元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | 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; } } |
从一开始,如果两个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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; } |
该方法仅为