2011-06-24 45 views
21

Tôi overrode the Equals() của lớp để so sánh giá trị ID của loại Guid.Cảnh báo: "... ghi đè Object.Equals (đối tượng o) nhưng không ghi đè lên Object.GetHashCode()"

Sau đó, Visual Studio cảnh báo:

... đè Object.equals (object o) nhưng không ghi đè Object.GetHashCode()

Vì vậy, tôi sau đó cũng gạt GetHashCode của nó () như thế này:

public partial class SomeClass 
{ 
    public override bool Equals(Object obj) 
    { 
     //Check for null and compare run-time types. 
     if (obj == null || this.GetType() != obj.GetType()) return false; 

     return this.Id == ((SomeClass)obj).Id; 
    } 

    public override int GetHashCode() 
    { 
     return this.Id.GetHashCode(); 
    } 
} 

Có vẻ như nó hoạt động. Tôi đã làm điều này một cách chính xác chưa? Ghi nhớ Id là loại Guid. Có quan trọng là lớp của tôi là một đối tượng Khung thực thể không?

Trả lời

6

Vì bạn không giao dịch với một lớp niêm phong, tôi khuyên bạn không nên kiểm tra mức độ bình đẳng của lớp như thế này . Bất kỳ sub-class của SomeClass sẽ có thể tham gia vào Equals cũng có, vì vậy bạn có thể muốn sử dụng này để thay thế:

if (obj as SomeClass == null) return false; 
+0

Khi nào một cá thể lớp con sẽ giống với một cá thể lớp cơ sở? Lớp con sẽ có các thuộc tính bổ sung và lớp cơ sở sẽ không biết về chúng khi so sánh. –

+3

'Equals' hiện được thực hiện để chỉ quan tâm đến' Id', tất cả các lớp con sẽ có. – recursive

+0

Tôi thừa nhận logic của bạn, nhưng như tôi đã chỉ ra trong bình luận của tôi với Eric, mã OP cho Equals đi thẳng ra khỏi một ví dụ từ [tài liệu MSDN chính thức] (http://msdn.microsoft.com/en-us/ library/bsc2ak47 (v = vs.110) .aspx). (Xem ví dụ về điểm lớp). – kmote

5

Theo truyền thống Equals được thực hiện theo cách như vậy mà hai đối tượng sẽ chỉ là "bình đẳng" nếu họ hoàn toàn giống nhau theo mọi cách. Ví dụ, nếu bạn có hai đối tượng đại diện cho cùng một đối tượng trong cơ sở dữ liệu, nhưng một đối tượng có thuộc tính khác nhau là Name thì đối tượng không được coi là "Bình đẳng" và nên tránh tạo cùng một "Hashcode" nếu có thể .

Tốt hơn là nên sai ở bên cạnh "không bằng" so với rủi ro khi gọi hai đối tượng bằng nhau. Đây là lý do tại sao việc thực hiện mặc định cho các đối tượng sử dụng vị trí bộ nhớ của chính đối tượng: không có hai đối tượng nào sẽ được coi là "bằng nhau" trừ khi chúng chính xác là cùng một đối tượng. Vì vậy, tôi muốn nói, trừ khi bạn muốn viết cả hai GetHashCodeEquals theo cách như vậy mà họ kiểm tra sự bình đẳng của tất cả các thuộc tính của họ, bạn tốt hơn không ghi đè một trong hai phương pháp.

Nếu bạn có cấu trúc dữ liệu (như HashSet), nơi bạn muốn xác định bình đẳng dựa trên giá trị ID, bạn có thể cung cấp triển khai IEqualityComparer cụ thể cho cấu trúc dữ liệu đó.

8

Có vẻ như chính xác với tôi. Bất cứ khi nào tôi làm một cái gì đó như thế này, tôi cũng thường thực hiện IEquatable để so sánh giữa các biến của cùng một loại thời gian biên dịch sẽ có hiệu quả hơn một chút.

public partial class SomeClass : IEquatable<SomeClass> 
{ 
    public override bool Equals(Object obj) 
    { 
     return Equals(obj as SomeClass); 
    } 
    public bool Equals(SomeClass obj) 
    { 
     if (obj == null) 
      return false; 
     return Id == obj.Id; 
    } 
    public override int GetHashCode() 
    { 
     return Id.GetHashCode(); 
    } 
} 

Cấu trúc này cũng cho phép một đối tượng có nguồn gốc giống với Id so sánh bằng với đối tượng ít được dẫn xuất. Nếu đây không phải là hành vi mong muốn, thì bạn cũng sẽ phải so sánh các loại như bạn làm trong câu hỏi.

if (obj.GetType() != typeof(SomeClass)) return false; 
29

Như những người khác đã nói, việc sử dụng Reflection in Equals có vẻ tinh tế. Bỏ qua một bên, hãy tập trung vào GetHashCode.

Quy tắc chính cho GetHashCode mà bạn không được vi phạm là nếu hai đối tượng bằng nhau thì cả hai đều phải có cùng mã băm. Hoặc, một cách tương đương để nói rằng là nếu hai đối tượng có mã băm khác nhau thì chúng phải là không bằng nhau. Triển khai của bạn có vẻ tốt ở đó.

Bạn được tự do vi phạm trò chuyện. Tức là, nếu hai đối tượng có cùng mã băm thì chúng được phép bằng nhau hoặc không bằng nhau, như bạn thấy phù hợp.

Tôi giả định rằng "Id" là thuộc tính không thể thay đổi. Nếu "Id" có thể thay đổi trong suốt thời gian tồn tại của đối tượng thì bạn có thể gặp sự cố khi đặt đối tượng vào bảng băm. Hãy xem xét đảm bảo rằng chỉ các thuộc tính bất biến được sử dụng trong tính toán bình đẳng và mã băm.

Triển khai của bạn có vẻ tốt nhưng thực tế bạn đang đặt câu hỏi cho biết rằng bạn có thể không nắm vững tất cả các yếu tố tinh tế đi vào xây dựng triển khai GetHashCode. Một nơi tốt để bắt đầu là bài viết của tôi về đề tài này:

http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/

+0

Tôi đã đọc bài viết của bạn, và đồng ý với các điểm của bạn, nhưng tôi thấy mình trong một tình huống mà tôi có một 'DataContract' chỉ có các thuộc tính có thể thay đổi. 'public string Day {get; bộ; } 'chẳng hạn. Dường như việc serialization tự động của .NET đòi hỏi các thuộc tính có thể thay đổi được. Phương thức 'Equals' của tôi nên xác định xem hai đối tượng có bằng nhau dựa trên các thuộc tính có thể thay đổi này hay không. Không tồn tại các thuộc tính bất biến. Vì vậy, tôi không thể tạo ra một phương thức 'GetHashCode' an toàn. Có vẻ như các tùy chọn của tôi không ghi đè 'Equals', hoặc không bao giờ đặt' DataContract' của tôi vào bảng băm ... – crush

+0

@crush: Bạn có tùy chọn thứ ba, là * không bao giờ biến đổi một đối tượng khi nó ở trong một bảng băm *. –

+1

Tôi đã đọc điều đó trong bài đăng của bạn, nhưng có vẻ như rất khó để đảm bảo rằng hợp đồng không bị hỏng. – crush

4

Bạn có câu trả lời xuất sắc cho câu hỏi đầu tiên của bạn:

Tôi đã thực hiện nó một cách chính xác?

Tôi sẽ trả lời câu hỏi thứ hai của bạn

Có vấn đề gì mà lớp học của tôi là một đối tượng Entity Framework?

Có vấn đề rất quan trọng. Khuôn khổ thực thể sử dụng HashSet rất nhiều nội bộ. Ví dụ: proxy động sử dụng HashSet để đại diện cho thuộc tính điều hướng bộ sưu tập và sử dụng s sử dụng EntityCollection mà lần lượt sử dụng HashSet nội bộ.

+1

+1: Vì vậy, đừng gây rối với mã băm trừ khi bạn hiểu cách mà Entity Framework đang mong họ hành xử. – StriplingWarrior

+0

Tôi đang tự hỏi liệu việc triển khai OP có cần sửa đổi trong ánh sáng câu trả lời của bạn hay không .. bạn nói điều đó quan trọng và tại sao nó quan trọng, nhưng không phải điều đó có nghĩa là những người thực hiện GetHashCode(). Bạn có thể cung cấp thêm một chút thông tin không? – Andrew

+0

@Andrew: Thông tin nằm trong các câu trả lời khác. Việc triển khai của OP không cần bất kỳ thay đổi nào. –

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