Đó là một thời gian dài, tuy nhiên tôi nghĩ rằng vẫn cần phải đưa ra một câu trả lời chính xác cho câu hỏi này, bao gồm cả những lời giải thích về các whys và hows. Câu trả lời tốt nhất cho đến nay là câu trích dẫn MSDN một cách mệt mỏi - đừng cố gắng đưa ra các quy tắc của riêng bạn, các anh chàng MS biết họ đang làm gì.
Nhưng điều đầu tiên trước tiên: Hướng dẫn như được trích dẫn trong câu hỏi là sai.
Bây giờ các whys - có hai trong số họ
Đầu tiên tại sao : Nếu mã băm được tính theo cách, thì nó không thay đổi trong suốt vòng đời của một đối tượng, ngay cả khi chính đối tượng đó thay đổi, hơn là nó sẽ phá vỡ hợp đồng bằng.
Hãy nhớ rằng: "Nếu hai đối tượng so sánh bằng nhau, phương thức GetHashCode cho mỗi đối tượng phải trả về cùng một giá trị. Tuy nhiên, nếu hai đối tượng không so sánh bằng nhau, thì các phương thức GetHashCode cho hai đối tượng không phải trả về các giá trị khác nhau."
Câu thứ hai thường bị hiểu sai là "Quy tắc duy nhất là, tại thời điểm tạo đối tượng, mã băm của các đối tượng bằng nhau phải bằng nhau". Không thực sự biết tại sao, nhưng đó là về bản chất của hầu hết các câu trả lời ở đây là tốt.
Hãy nghĩ về hai đối tượng chứa một tên, trong đó tên được sử dụng trong phương thức bằng: Tên giống nhau -> cùng một thứ. Tạo sơ thẩm A: Name = Joe Tạo sơ thẩm B: Name = Peter
Hashcode A và Hashcode B rất có thể sẽ không giống nhau. Điều gì sẽ xảy ra, khi Tên của trường hợp B được đổi thành Joe?
Theo hướng dẫn từ câu hỏi, mã băm của B sẽ không thay đổi. Kết quả của việc này sẽ là: A.Equals (B) ==> true Nhưng đồng thời: A.GetHashCode () == B.GetHashCode () ==> false.
Nhưng chính xác hành vi này bị cấm rõ ràng bởi hợp đồng bằng & hashcode-hợp đồng.
Thứ hai tại sao : Mặc dù - dĩ nhiên - đúng, những thay đổi trong mã băm có thể phá vỡ danh sách băm và các đối tượng khác bằng cách sử dụng mã băm, điều ngược lại cũng đúng. Không thay đổi mã băm trong trường hợp xấu nhất sẽ nhận được danh sách băm, trong đó tất cả rất nhiều đối tượng khác nhau sẽ có cùng mã băm và do đó trong cùng một thùng băm - xảy ra khi các đối tượng được khởi tạo với giá trị tiêu chuẩn, chẳng hạn.
Bây giờ đến với các cung, Vâng, thoạt nhìn, dường như có một mâu thuẫn - dù bằng cách nào, mã sẽ bị phá vỡ. Nhưng không có vấn đề gì đến từ mã băm thay đổi hoặc không thay đổi.
Nguồn gốc của các vấn đề được mô tả tốt trong MSDN:
Từ mục nhập hashtable của MSDN:
Các đối tượng chính phải là bất biến miễn là chúng được sử dụng làm khóa trong Hashtable.
Điều này không có nghĩa là:
Bất kỳ đối tượng nào tạo ra giá trị băm đều phải thay đổi giá trị băm, khi đối tượng thay đổi, nhưng nó không được - tuyệt đối không được - cho phép mọi thay đổi đối với chính nó, khi nó được sử dụng bên trong Hashtable (hoặc bất kỳ đối tượng sử dụng Hash nào khác), tất nhiên) .
Đầu tiên, cách dễ nhất dĩ nhiên là thiết kế các đối tượng bất biến chỉ để sử dụng trong hashtables, sẽ được tạo ra như bản sao của các đối tượng bình thường, có thể thay đổi khi cần. Bên trong các đối tượng không thay đổi, thật tuyệt vời khi lưu trữ mã băm, vì nó không thay đổi.
Cách thứ hai Hoặc cung cấp cho đối tượng một "bạn đã được băm ngay bây giờ" -flag, đảm bảo tất cả dữ liệu đối tượng là riêng tư, kiểm tra cờ trong tất cả các chức năng có thể thay đổi dữ liệu đối tượng và ném dữ liệu ngoại lệ nếu không cho phép thay đổi (ví dụ: cờ được đặt ). Bây giờ, khi bạn đặt đối tượng vào bất kỳ khu vực băm nào, hãy đảm bảo đặt cờ và - cũng như - bỏ đặt cờ, khi không còn cần thiết nữa. Để dễ sử dụng, tôi khuyên bạn nên đặt cờ tự động bên trong phương thức "GetHashCode" - theo cách này không thể quên được. Và cuộc gọi rõ ràng của phương thức "ResetHashFlag" sẽ đảm bảo rằng lập trình viên sẽ phải suy nghĩ, khi đó nó có hoặc không được phép thay đổi dữ liệu đối tượng.
Ok, cũng nên nói: Có những trường hợp, trong đó có thể có các đối tượng có dữ liệu có thể thay đổi, trong đó mã băm vẫn không thay đổi, khi dữ liệu đối tượng bị thay đổi, mà không vi phạm hợp đồng bằng & hashcode-hợp đồng.
Tuy nhiên, điều này không yêu cầu, phương thức đẳng thức cũng không dựa trên dữ liệu có thể thay đổi. Vì vậy, nếu tôi viết một đối tượng và tạo phương thức GetHashCode chỉ tính toán một giá trị một lần và lưu trữ bên trong đối tượng để trả về nó trong các cuộc gọi sau, thì tôi phải, một lần nữa: hoàn toàn phải, tạo phương thức Equals, sẽ sử dụng các giá trị được lưu trữ để so sánh, do đó A.Equals (B) sẽ không bao giờ thay đổi từ sai thành đúng. Nếu không, hợp đồng sẽ bị phá vỡ. Kết quả của điều này thường sẽ là phương thức Equals không có ý nghĩa gì - nó không phải là tham chiếu ban đầu bằng, nhưng nó cũng không phải là một giá trị bằng. Đôi khi, đây có thể là hành vi dự định (tức là hồ sơ khách hàng), nhưng thường thì không.
Vì vậy, chỉ cần thay đổi kết quả GetHashCode, khi dữ liệu đối tượng thay đổi và nếu việc sử dụng đối tượng bên trong hàm băm sử dụng danh sách hoặc đối tượng được dự định (hoặc chỉ có thể) thì làm cho đối tượng bất biến hoặc tạo cờ chỉ đọc để sử dụng cho vòng đời của một danh sách băm chứa đối tượng.
(Nhân tiện: Tất cả những thứ này không phải là C # oder .NET cụ thể - đó là bản chất của tất cả các triển khai có thể băm, hoặc nói chung là của bất kỳ danh sách được lập chỉ mục nào, việc xác định dữ liệu của các đối tượng sẽ không bao giờ thay đổi, trong khi đối tượng nằm trong danh sách Hành vi bất ngờ và không thể đoán trước sẽ xảy ra, nếu quy tắc này bị phá vỡ. Ở đâu đó, có thể có các triển khai danh sách, theo dõi tất cả các yếu tố trong danh sách và tự động lập lại danh sách - nhưng hiệu suất của những điều đó chắc chắn sẽ rất kinh khủng.)