9

2 つの PropertyInfo を Equals() と比較するコードがいくつかあります。これは通常は機能しているように見えますが、同じ基本プロパティの 2 つの反映されたプロパティ情報オブジェクトが等しくないという奇妙な状況に遭遇しました。

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() が常に表示されます。これは PropertyInfos の場合ではありませんか?

その他の注意事項: - この奇妙な現象は、.NET 4、VS2012、x86 ビルド ターゲットで NUnit テストを実行したときに発生しました - これは、この方法で比較したすべてのプロパティで発生するわけではありませんが、1 つのプロパティで一貫して失敗します。

誰でもこの動作を説明できますか?

編集: 誰かが興味を持っている場合に備えて、MemberInfos を比較するために私が書いた EqualityComparison 関数を次に示します。

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;
    }
}
4

2 に答える 2

14

オブジェクト ID は Type クラスに対してのみ保証され、他のリフレクション クラスに対しては保証されません。同等性を比較する適切な方法は、プロパティが同じメタデータ トークンを持ち、同じモジュールからのものであることを確認することです。だからこれを試してください:

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

これは、ecma 335 が適用される限り理にかなっています。あなたが投稿しなかったので、あなたのコードに対してこれをテストできませんでした。だから試してみてください。

于 2012-11-28T23:42:15.203 に答える
7

私は彼らが異なっていると推測していますReflectedType。たとえば、継承:

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

と を見てtypeof(A).GetProperty("Foo")くださいtypeof(B).GetProperty("Foo")

于 2012-11-28T23:06:16.753 に答える