C# 这是.Net反射中的错误吗?
答案是:不,这不是一个bug。 区别在于不同的地方 所以这里真正的问题是:有没有一种方法可以比较两个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
属性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;
}