2009-03-04 26 views
6

Nếu tôi ghi đè Bằng và GetHashCode, làm cách nào để quyết định trường nào cần so sánh? Và điều gì sẽ xảy ra nếu tôi có hai đối tượng với hai trường mỗi, nhưng Equals chỉ kiểm tra một trường?Ghi đè bằng() nhưng không kiểm tra tất cả các trường - điều gì sẽ xảy ra?

Nói cách khác, chúng ta hãy nói rằng tôi có lớp học này:

class EqualsTestClass 
{ 
    public string MyDescription { get; set; } 
    public int MyId { get; set; } 

    public override bool Equals(object obj) 
    { 
     EqualsTestClass eq = obj as EqualsTestClass; 
     if(eq == null) { 
      return false; 
     } else { 
      return MyId.Equals(eq.MyId); 
     } 
    } 

    public override int GetHashCode() 
    { 
     int hashcode = 23; 
     return (hashcode * 17) + MyId.GetHashCode(); 
    } 
} 

tôi xem xét hai đối tượng bình đẳng nếu họ có myid cùng. Vì vậy, nếu Id bằng nhau nhưng mô tả là khác nhau, chúng vẫn được coi là bằng nhau.

Tôi tự hỏi những cạm bẫy của phương pháp này là gì? Tất nhiên, một cấu trúc như thế này sẽ hoạt động như mong đợi:

 List<EqualsTestClass> test = new List<EqualsTestClass>(); 

     EqualsTestClass eq1 = new EqualsTestClass(); 
     eq1.MyId = 1; 
     eq1.MyDescription = "Des1"; 

     EqualsTestClass eq2 = new EqualsTestClass(); 
     eq2.MyId = 1; 
     eq2.MyDescription = "Des2"; 

     test.Add(eq1); 
     if (!test.Contains(eq2)) 
     { 
      // Will not be executed, as test.Contains is true 
      test.Add(eq2); 
     } 

Vì eq2 có giá trị bằng eq1, nó sẽ không được thêm vào. Nhưng đó là mã mà tôi kiểm soát, nhưng tôi tự hỏi nếu có mã trong khuôn khổ có thể gây ra các vấn đề bất ngờ? Vì vậy, tôi nên luôn luôn thêm tất cả các lĩnh vực công cộng trong so sánh Equals() của tôi, hoặc các hướng dẫn để tránh một bất ngờ khó chịu vì một số Framework-Mojo xấu là hoàn toàn bất ngờ là gì?

Trả lời

11

Lý do ghi đè Equals() là bạn xác định, ý nghĩa của hai trường hợp là bằng nhau. Trong một số trường hợp có nghĩa là tất cả các trường phải bằng nhau, nhưng nó không phải. Bạn quyết định.

Để biết thêm thông tin, hãy xem documentation và điều này post.

+0

đồng ý, giống như OP, tôi đã overidden Equals() để chỉ cần kiểm tra một ID chính, và thats nó, hoàn toàn của nó lập trình gọi. –

1

Tôi không nghĩ bạn cần phải lo lắng về Khung trong trường hợp này. Nếu bạn là Nhà thiết kế lớp xem xét hai trường hợp của lớp đó bằng nhau nếu chúng chia sẻ cùng MyId, thì bạn chỉ cần kiểm tra MyId trong các phương thức Equals() và GetHashCode() đã được overriden của bạn.

1

Bạn chỉ cần kiểm tra các trường được yêu cầu để khớp, nếu tất cả những gì cần phải khớp là ID thì hãy đi theo đó.

1

Câu hỏi: Nếu tôi ghi đè Bằng và GetHashCode, làm cách nào để quyết định trường nào tôi so sánh?

Tùy thuộc vào những gì bạn đang cố gắng hoàn thành. Nếu bạn đang cố gắng để xem nếu các đối tượng là chính xác giống nhau, bạn nên so sánh tất cả chúng. Nếu bạn có một số 'chìa khóa' và bạn chỉ muốn biết nếu chúng là cùng một 'đối tượng', ngay cả khi dữ liệu khác là khác nhau thì chỉ cần kiểm tra các giá trị 'chìa khóa'.

Và điều gì sẽ xảy ra nếu tôi có hai đối tượng với hai trường mỗi trường, nhưng chỉ kiểm tra một trường?

Sau đó, bạn sẽ có phương pháp bình đẳng chỉ kiểm tra xem 'khóa' có giống nhau hay không và có khả năng có nhiều đối tượng 'bằng' có phương sai nội bộ.

1

Những người khác đã nói điều này hoàn toàn hợp lệ và được mong đợi, và đó chính xác là cách Equals được cho là hoạt động. Vì vậy, không có vấn đề với nó như là một lớp học.

Tôi rất thận trọng về điều này như một API. Tha thứ cho tôi nếu nó không phải là những gì đã được dự định: trong trường hợp đó đây chỉ là một lưu ý thận trọng cho người khác.

Vấn đề tiềm năng là người dùng API sẽ tự nhiên mong đợi các đối tượng bằng nhau "giống nhau". Đây không phải là một phần của hợp đồng bình đẳng, nhưng nó là một phần của ý nghĩa thông thường của từ này. Lớp này trông giống như một bộ nhị phân, nhưng không phải là một, vì vậy đó là lý do hợp lý.

Ví dụ về lý do hợp lý như vậy là một trường là "chi tiết triển khai có thể nhìn thấy", giống như hệ số tải tối đa trên vùng chứa dựa trên thẻ bắt đầu bằng #. Một ví dụ về một lý do rủi ro (mặc dù hấp dẫn) là "bởi vì tôi đã thêm mô tả vào sau đó và không muốn thay đổi phương thức Equals trong trường hợp nó phá vỡ một cái gì đó".

Vì vậy, nó hoàn toàn hợp lệ để làm điều gì đó hơi phản trực giác, đặc biệt nếu bạn ghi rõ hành vi có thể gây ngạc nhiên. Các phương thức Equals như vậy phải được hỗ trợ, bởi vì cấm chúng sẽ bị điên trong trường hợp một trường rõ ràng là không liên quan. Nhưng phải rõ ràng tại sao nó tạo ra hai cặp ID-description với cùng một ID và các mô tả khác nhau, nhưng nó không có ý nghĩa để thêm chúng vào một thùng chứa (như HashSet) sử dụng Equals/HashCode để ngăn trùng lặp mục.

0

Ví dụ của bạn đang kiểm tra xem danh sách (của EqualsTestClass) có chứa một đối tượng cùng loại với cùng giá trị thuộc tính hay không. Một cách khác để thực hiện tác vụ này mà không ghi đè bằng (và sự hiểu biết truyền thống về bằng) là sử dụng một trình so sánh tùy chỉnh. Nó sẽ giống như thế này (trong VB):

Public Class EqualsTestComparer 
Implements IEqualityComparer(Of EqualsTestClass) 
Public Function Equals1(ByVal x As EqualsTestClass, ByVal y As EqualsTestClass) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).Equals 
    If x.MyId = y.MyId and x.MyDescription = y.MyDescription Then 
     Return True 
    Else 
     Return False 
    End If 
End Function 

Public Function GetHashCode1(ByVal obj As EqualsTestClass) As Integer Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).GetHashCode 
    Return obj.ToString.ToLower.GetHashCode 
End Function  
End Class 

Sau đó, trong thói quen của bạn, bạn chỉ cần sử dụng comparer tùy chỉnh:

If Not test.Contains(eq2, New EqualsTestComparer) Then 
    //Do Stuff 
End if 
Các vấn đề liên quan