.NET 属性信息的相等性



我有一些代码可以将 2 个属性信息与 Equals() 进行比较。虽然这通常似乎有效,但我遇到了一个奇怪的情况,即同一基础属性的两个反射属性信息对象不相等:

PropertyInfo prop1, prop2; // both are public and not static
Console.WriteLine(prop1 == prop2); // false ???
Console.WriteLine(Equals(prop1, prop2)); // false ???
Console.WriteLine(prop1.DeclaringType == prop2.DeclaringType); // true
Console.WriteLine(prop1.ReturnType == prop2.ReturnType); // true
Console.WriteLine(prop1.Name == prop2.Name); // true
Console.WriteLine(prop1.DeclaringType.GetProperties().Contains(prop1)); // true
Console.WriteLine(prop2.DeclaringType.GetProperties().Contains(prop2)); // false ???

看起来 PropertyInfo 实际上并没有实现 Equals(),但我认为 .NET 缓存反射成员,以便始终返回相同的实例。你肯定会一直看到 a.GetType() == b.GetType()。物业信息不是这样吗?

其他一些注意事项:- 在 .NET 4、VS2012、x86 构建目标中运行 NUnit 测试时发生了这种奇怪现象-这甚至不会发生在我们以这种方式比较的所有属性中,但它在一个属性上始终失败。

谁能解释这种行为?

编辑:如果有人感兴趣,这是我写的用于比较会员信息的平等比较功能:

public class MemberEqualityComparer : EqualityComparer<MemberInfo> {
    public override bool Equals(MemberInfo @this, MemberInfo that) {
        if (@this == that) { return true; }
        if (@this == null || that == null) { return false; }
                        // handles everything except for generics
                    if (@this.MetadataToken != that.MetadataToken
                        || !Equals(@this.Module, that.Module)
                        || this.Equals(@this.DeclaringType, that.DeclaringType))
                    {
                        return false;
                    }
                    bool areEqual;
                    switch (@this.MemberType)
                    {
                        // constructors and methods can be generic independent of their types,
                        // so they are equal if they're generic arguments are equal
                        case MemberTypes.Constructor:
                        case MemberTypes.Method:
                            var thisMethod = @this as MethodBase;
                            var thatMethod = that as MethodBase;
                                                areEqual = thisMethod.GetGenericArguments().SequenceEqual(thatMethod.GetGenericArguments(), 
this);
                            break;
                        // properties, events, and fields cannot be generic independent of their types,
                        // so if we've reached this point without bailing out we just return true.
                        case MemberTypes.Property:
                        case MemberTypes.Event:
                        case MemberTypes.Field:
                            areEqual = true;
                            break;
                        // the system guarantees reference equality for types, so if we've reached this point
                        // without returning true the two are not equal
                        case MemberTypes.TypeInfo:
                        case MemberTypes.NestedType:
                            areEqual = false;
                            break;
                        default:
                            throw new NotImplementedException(@this.MemberType.ToString());
    }
    public override int GetHashCode(MemberInfo memberInfo) {
        if (memberInfo == null) { return 0; }
    var hash = @this.MetadataToken 
        ^ @this.Module.GetHashCode() 
        ^ this.GetHashCode(@this.DeclaringType);
    return hash;
    }
}

对象标识只承诺用于 Type 类,而不承诺用于其他反射类。 比较相等性的一种可能合理的方法是检查属性是否具有相同的元数据标记并且来自同一模块。 所以试试这个:

bool equal = prop1.MetadataToken == prop2.MetadataToken &&
             prop1.Module.Equals(prop2.Module);

只要 ecma 335 适用,这是有道理的。 我无法针对您的代码对此进行测试,因为您没有发布它。 所以试试吧。

我猜他们有不同的ReflectedType.例如,继承:

class A {
   public int Foo {get;set;}
}
class B : A {}

现在看看typeof(A).GetProperty("Foo")typeof(B).GetProperty("Foo").

相关内容

  • 没有找到相关文章

最新更新