2013-05-13 32 views
9

Cho hai giống hệt đối tượng loại vô danh:Có an toàn khi sử dụng GetHashCode để so sánh các loại Anonymous giống nhau không?

{msg:"hello"} //anonType1 
{msg:"hello"} //anonType2 

Và giả định rằng họ đã không được giải quyết đến cùng loại (ví dụ như họ có thể được định nghĩa trong hội đồng khác nhau)

anonType1.Equals(anonType2); //false 

Hơn nữa, giả sử rằng tại thời gian biên dịch, tôi không thể lấy cấu trúc của một (giả sử anonType1) vì API chỉ hiển thị object

Vì vậy, để so sánh chúng, tôi nghĩ về các kỹ thuật sau:

  1. Sử dụng phản ánh để có được thuộc tính msg trên anonType1 để so sánh.
  2. Cast anonType1 cho một loại dynamic và tài liệu tham khảo .msg trên thành viên năng động để so sánh
  3. Hãy so sánh kết quả của .GetHashCode() trên từng đối tượng.

Câu hỏi của tôi là: Có an toàn khi sử dụng Tùy chọn 3 không? I E. có hợp lý khi giả định rằng việc triển khai .GetHashcode() sẽ luôn trả về cùng một giá trị cho các loại ẩn danh có cấu trúc, nhưng khác nhau trong phiên bản hiện tại và tất cả các phiên bản tương lai của khung công tác .NET.

+0

Lưu ý: tôi đã thêm một 'Expression' dựa trên memberwise Comparer - có thể có ích –

+0

Brilliant, cảm ơn. –

Trả lời

5

Câu hỏi thú vị. Đặc điểm kỹ thuật xác định rằng các phương thức EqualsGetHashcode (lưu ý lỗi đánh máy trong đặc điểm kỹ thuật!) Sẽ hoạt động đối với các thể hiện cùng loại, tuy nhiên việc triển khai không được xác định. Khi nó xảy ra, trình biên dịch MS C# hiện tại thực hiện điều này bằng cách sử dụng các số ma thuật như một hạt giống của -1134271262 và một số nhân là -1521134295. Nhưng rằng không phải là một phần của đặc điểm kỹ thuật. Về mặt lý thuyết có thể thay đổi triệt để giữa các phiên bản trình biên dịch C# và nó vẫn sẽ đáp ứng những gì nó cần. Vì vậy, nếu 2 hội đồng không được biên dịch bởi trình biên dịch tương tự, không có bảo đảm. Thật vậy, nó sẽ là "hợp lệ" (nhưng không chắc) cho trình biên dịch để nghĩ ra một giá trị hạt giống mới mỗi khi nó biên dịch.

Cá nhân, tôi sẽ xem xét sử dụng kỹ thuật IL hoặc Expression để thực hiện việc này. So sánh các đối tượng tương tự như hình tượng thành viên theo tên khá dễ làm với Expression.

Đối với thông tin, tôi cũng đã xem xét cách mcs (trình biên dịch Mono) thực hiện GetHashCodenó khác; thay vì hạt giống và nhân, nó sử dụng một sự kết hợp của hạt giống, xor, nhân, ca và bổ sung. Vì vậy, cùng loại được biên soạn bởi Microsoft và Mono sẽ có rất khác nhau GetHashCode.

static class Program { 
    static void Main() { 
     var obj = new { A = "abc", B = 123 }; 
     System.Console.WriteLine(obj.GetHashCode()); 
    } 
} 
  • Mono: -2077468848
  • Microsoft: -617335881

Về cơ bản, tôi không nghĩ rằng bạn có thể đảm bảo điều này.


Làm thế nào về:

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 
class Foo 
{ 
    public string A { get; set; } 
    public int B; // note a field! 
    static void Main() 
    { 
     var obj1 = new { A = "abc", B = 123 }; 
     var obj2 = new Foo { A = "abc", B = 123 }; 
     Console.WriteLine(MemberwiseComparer.AreEquivalent(obj1, obj2)); // True 

     obj1 = new { A = "abc", B = 123 }; 
     obj2 = new Foo { A = "abc", B = 456 }; 
     Console.WriteLine(MemberwiseComparer.AreEquivalent(obj1, obj2)); // False 

     obj1 = new { A = "def", B = 123 }; 
     obj2 = new Foo { A = "abc", B = 456 }; 
     Console.WriteLine(MemberwiseComparer.AreEquivalent(obj1, obj2)); // False 
    } 

} 

public static class MemberwiseComparer 
{ 
    public static bool AreEquivalent(object x, object y) 
    { 
     // deal with nulls... 
     if (x == null) return y == null; 
     if (y == null) return false; 
     return AreEquivalentImpl((dynamic)x, (dynamic)y); 
    } 
    private static bool AreEquivalentImpl<TX, TY>(TX x, TY y) 
    { 
     return AreEquivalentCache<TX, TY>.Eval(x, y); 
    } 
    static class AreEquivalentCache<TX, TY> 
    { 
     static AreEquivalentCache() 
     { 
      const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; 
      var xMembers = typeof(TX).GetProperties(flags).Select(p => p.Name) 
       .Concat(typeof(TX).GetFields(flags).Select(f => f.Name)); 
      var yMembers = typeof(TY).GetProperties(flags).Select(p => p.Name) 
       .Concat(typeof(TY).GetFields(flags).Select(f => f.Name)); 
      var members = xMembers.Intersect(yMembers); 

      Expression body = null; 
      ParameterExpression x = Expression.Parameter(typeof(TX), "x"), 
           y = Expression.Parameter(typeof(TY), "y"); 
      foreach (var member in members) 
      { 
       var thisTest = Expression.Equal(
        Expression.PropertyOrField(x, member), 
        Expression.PropertyOrField(y, member)); 
       body = body == null ? thisTest 
        : Expression.AndAlso(body, thisTest); 
      } 
      if (body == null) body = Expression.Constant(true); 
      func = Expression.Lambda<Func<TX, TY, bool>>(body, x, y).Compile(); 
     } 
     private static readonly Func<TX, TY, bool> func; 
     public static bool Eval(TX x, TY y) 
     { 
      return func(x, y); 
     } 
    } 
} 
+0

Nếu có một đặc tả xác định chính xác các lớp ẩn danh mà trình biên dịch phải tạo, thì khả năng tương tác sẽ là tầm thường. Nếu không có một spec cho những gì các lớp học tạo ra trình biên dịch nên trông giống như, tôi thấy không có cách nào các lớp học như vậy có thể tương thích ngay cả khi họ muốn được. Thực tế là một số lớp tùy ý có các thuộc tính có tên và giá trị khớp với các thuộc tính của một lớp ẩn danh không có nghĩa là một cá thể của lớp trước nên so sánh với một cá thể của thuộc tính sau. Mặt khác, – supercat

+0

@supercat, bạn không có khả năng so sánh 2 đối tượng nếu chúng không tương đương * ngữ nghĩa * giả giữa chúng –

+0

Đối với tất cả các giá trị 'X' và' Y' không có giá trị không ghi đè of 'Equals (object)', '((đối tượng) X) .Equals (Y)' và '((đối tượng) Y) .Equals (X)' luôn luôn trả về cùng một giá trị, không có ngoại lệ. Có một loại báo cáo các cá thể của nó bằng với những thứ của một số kiểu không liên quan mà không biết gì về nó là gây ra các bộ sưu tập như 'Từ điển ' bị hỏng nếu đối tượng của cả hai loại được lưu trữ trong đó. – supercat

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