it-swarm-vi.com

Làm thế nào để bạn triển khai GetHashCode cho cấu trúc với hai chuỗi, khi cả hai chuỗi có thể hoán đổi cho nhau

Tôi có một cấu trúc trong C #:

public struct UserInfo
{
   public string str1
   {
     get;
     set;
   }

   public string str2
   {
     get;
     set;
   }   
}

Quy tắc duy nhất là UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))

Làm cách nào để ghi đè hàm GetHashCode cho cấu trúc này?

66
Graviton

MSD :

Hàm băm phải có các thuộc tính sau:

  • 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, 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.
  • Phương thức GetHashCode cho một đối tượng phải luôn trả về cùng mã băm miễn là không có sửa đổi nào đối với trạng thái đối tượng xác định giá trị trả về của phương thức Equals của đối tượng. Lưu ý rằng điều này chỉ đúng với thực thi hiện tại của một ứng dụng và mã băm khác có thể được trả về nếu ứng dụng được chạy lại.
  • Để có hiệu suất tốt nhất, hàm băm phải tạo phân phối ngẫu nhiên cho tất cả đầu vào.

Đưa nó vào tài khoản một cách chính xác là:

return str1.GetHashCode() ^ str2.GetHashCode() 

^ có thể được thay thế bằng hoạt động giao hoán khác

66
aku

Xem câu trả lời của Jon Skeet - hoạt động nhị phân như ^ không tốt, chúng sẽ thường tạo ra băm va chạm!

26
Tomáš Kafka
public override int GetHashCode()
{
    unchecked
    {
        return (str1 ?? String.Empty).GetHashCode() +
            (str2 ?? String.Empty).GetHashCode();
    }
}

Sử dụng toán tử '+' có thể tốt hơn so với sử dụng '^', bởi vì mặc dù bạn rõ ràng muốn ('AA', 'BB') và ('BB', 'AA') giống nhau, nhưng bạn có thể không muốn ( 'AA', 'AA') và ('BB', 'BB') giống nhau (hoặc tất cả các cặp bằng nhau cho vấn đề đó).

Quy tắc 'càng nhanh càng tốt' không hoàn toàn được tuân thủ trong giải pháp này bởi vì trong trường hợp null, nó thực hiện một 'GetHashCode ()' trên chuỗi trống thay vì trả về một hằng số đã biết, nhưng ngay cả khi không đo lường rõ ràng tôi sẵn sàng để mạo hiểm đoán rằng sự khác biệt sẽ không đủ lớn để lo lắng trừ khi bạn mong đợi rất nhiều null.

15
Jerry
  1. Theo nguyên tắc chung, một cách đơn giản để tạo mã băm cho một lớp là XOR tất cả các trường dữ liệu có thể tham gia vào việc tạo mã băm (cẩn thận kiểm tra null như được chỉ ra bởi khác). Điều này cũng đáp ứng yêu cầu (nhân tạo?) rằng các mã băm cho UserInfo ("AA", "BB") và UserInfo ("BB", "AA") giống nhau.

  2. Nếu bạn có thể đưa ra các giả định về việc sử dụng lớp của mình, có lẽ bạn có thể cải thiện hàm băm của mình. Ví dụ: nếu thông thường thì str1 và str2 giống nhau, XOR có thể không phải là một lựa chọn tốt. Nhưng nếu str1 và str2 đại diện, hãy nói, tên và họ, XOR có lẽ là một lựa chọn tốt.

Mặc dù đây rõ ràng không phải là một ví dụ trong thế giới thực, nhưng có thể đáng để chỉ ra rằng: - Đây có lẽ là một ví dụ kém về việc sử dụng một cấu trúc: Một cấu trúc thường có ngữ nghĩa giá trị, dường như không phải là trường hợp ở đây - Sử dụng các thuộc tính với setters để tạo mã băm cũng đang yêu cầu sự cố.

5
Joe

Một cách đơn giản chung là làm điều này:

return string.Format("{0}/{1}", str1, str2).GetHashCode();

Trừ khi bạn có yêu cầu hiệu suất nghiêm ngặt, đây là cách dễ nhất tôi có thể nghĩ đến và tôi thường xuyên sử dụng phương pháp này khi tôi cần một khóa tổng hợp. Nó xử lý các trường hợp null tốt và sẽ không gây ra (m) bất kỳ va chạm băm nào (nói chung). Nếu bạn mong đợi '/' trong chuỗi của mình, chỉ cần chọn một dấu tách khác mà bạn không mong đợi.

4
Daniel Lidström

Đi dọc theo dòng ReSharper là gợi ý:

public int GetHashCode()
{
    unchecked
    {
        int hashCode;

        // String properties
        hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0);
        hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0);

        // int properties
        hashCode = (hashCode * 397) ^ intProperty;
        return hashCode;
    }
}

397 là một số nguyên tố có kích thước đủ để làm cho biến kết quả tràn và trộn các bit của hàm băm một chút, cung cấp phân phối mã băm tốt hơn. Mặt khác, không có gì đặc biệt trong 397 phân biệt nó với các số nguyên tố khác có cùng độ lớn.

3
Jani Hyytiäinen
public override int GetHashCode()   
{       
    unchecked      
    {           
        return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0);       
    }   
}
3
user11556

À đúng, như Gary Shutler đã chỉ ra:

return str1.GetHashCode() + str2.GetHashCode();

Có thể tràn. Bạn có thể thử truyền miễn là đề xuất của Artem hoặc bạn có thể bao quanh câu lệnh trong từ khóa không được kiểm tra:

return unchecked(str1.GetHashCode() + str2.GetHashCode());
2
Grokys

Hãy thử cái này:

(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()
1
Artem Tikhomirov

Nhiều khả năng. Ví dụ.

return str1.GetHashCode() ^ str1.GetHashCode()

0
VolkerK

Có lẽ một cái gì đó như str1.GetHashCode () + str2.GetHashCode ()? hoặc (str1.GetHashCode () + str2.GetHashCode ())/2? Bằng cách này, nó sẽ giống nhau bất kể str1 và str2 có bị tráo đổi không ....

0
Mike Stone

Sắp xếp chúng, sau đó nối chúng:

[.___. return
0
Steve Morgan

Kết quả của GetHashCode được cho là:

  1. Nhanh nhất có thể.
  2. Càng độc đáo càng tốt.

Mang những điều đó trong tâm trí, tôi sẽ đi với một cái gì đó như thế này:

if (str1 == null)
    if (str2 == null)
        return 0;
    else
       return str2.GetHashCode();
else
    if (str2 == null)
        return str1.GetHashCode();
    else
       return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();

Chỉnh sửa : Quên null. Mã cố định.

0
Omer van Kloeten