2012-11-28 34 views
6

Tôi có một số mã so sánh 2 PropertyInfos với Equals(). Trong khi điều này thường dường như làm việc, tôi đã chạy vào một tình huống kỳ lạ nơi hai phản ánh thông tin thuộc tính đối tượng cho các tài sản cơ bản giống nhau không bằng nhau:Sự bình đẳng cho .NET PropertyInfos

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 ??? 

Dường như PropertyInfo không thực sự thực hiện Equals(), nhưng tôi nghĩ rằng bộ đệm .NET phản ánh các thành viên sao cho cùng một cá thể luôn được trả về. Bạn chắc chắn thấy a.GetType() == b.GetType() mọi lúc. Đây có phải là trường hợp của PropertyInfos không?

Một số lưu ý khác: -Đây weirdness xảy ra khi chạy một kiểm tra NUnit trong .NET 4, VS2012, x86 xây dựng mục tiêu -Đây thậm chí không xảy ra cho tất cả các thuộc tính chúng ta so sánh cách này, nhưng nó không thành công liên tục trên một bất động sản.

Có ai có thể giải thích hành vi này không?

EDIT: trong trường hợp có ai quan tâm, đây là chức năng EqualityComparison tôi đã viết để so sánh MemberInfos:

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; 
    } 
} 
+0

bạn đã thấy mã được biên dịch chưa? – pylover

+0

@smartcaveman, bạn có ý định trả về false bất cứ khi nào các loại khai báo bằng nhau không? Tôi nghĩ rằng nên có một "không" trong đó. Ngoài ra, vài dòng cuối cùng của phương thức Equals của bạn dường như bị cắt bỏ, vì không có câu lệnh trả về hoặc dấu ngoặc đóng. – Jax

Trả lời

5

Tôi đoán họ có một khác nhau ReflectedType. Ví dụ: kế thừa:

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

giờ hãy xem typeof(A).GetProperty("Foo")typeof(B).GetProperty("Foo").

12

Danh tính đối tượng chỉ được hứa cho lớp Loại, không phải cho các lớp phản chiếu khác. Một có thể là cách âm thanh để so sánh cho sự bình đẳng là để kiểm tra xem các thuộc tính có cùng mã thông báo siêu dữ liệu và đến từ cùng một mô-đun hay không. Vì vậy, hãy thử điều này:

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

Có nghĩa là miễn là ecma 335 áp dụng. Tôi không thể kiểm tra điều này đối với mã của bạn vì bạn không đăng nó. Vì vậy, hãy thử nó.

+0

Điều này dường như hoạt động, ngoại trừ các thuộc tính của các loại chung (trừ khi bạn muốn Danh sách .Count == Danh sách .Count). – ChaseMedallion

+1

@ChaseMedallion, Làm thế nào về việc thêm một kết hợp với 'prop1.DeclaringType == prop2.DeclaringType'? Có vẻ như vậy sẽ bao gồm trường hợp đó – smartcaveman

+0

@smartcaveman: DeclaringType xử lý các thuộc tính generics tốt, vì bản thân các thuộc tính không thể là generic (chỉ là kiểu khai báo). Tuy nhiên, để mở rộng phương thức này cho tất cả các thành viên, bạn cần thực hiện một số kiểm tra bổ sung để xử lý các phương thức chung. Xem mã đã đăng của tôi (trong câu hỏi) cho một bộ kiểm tra hoàn chỉnh (dường như) cho các thành viên tùy ý. Bạn sẽ thấy DeclaringType kiểm tra về phía trên cùng. – ChaseMedallion

Các vấn đề liên quan