Tại sao Visual Studio bổ sung thêm -1937169414 vào một tính toán mã băm được tạo?


9

Nếu bạn sử dụng menu tái cấu trúc riêng của Visual Studio để thêm triển khai GetHashCode vào một lớp như thế này:

Tạo menu GetHashCode

và chọn thuộc tính int duy nhất trong lớp:

Màn hình lựa chọn thành viên

nó tạo mã này trên .NET Framework:

public override int GetHashCode()
{
    return -1937169414 + Value.GetHashCode();
}

( HashCode.Combine(Value)thay vào đó, nó tạo ra trên .NET Core, điều mà tôi không chắc là nó có cùng giá trị không)

Điều gì đặc biệt về giá trị này? Tại sao Visual Studio không sử dụng Value.GetHashCode()trực tiếp? Theo tôi hiểu, nó không thực sự ảnh hưởng đến phân phối băm. Vì chỉ là bổ sung, các giá trị liên tiếp vẫn sẽ tích lũy cùng nhau.

EDIT: Tôi chỉ thử điều này với các lớp khác nhau với Valuecác thuộc tính nhưng rõ ràng tên thuộc tính ảnh hưởng đến số được tạo. Chẳng hạn, nếu bạn đổi tên tài sản thành Halue, số sẽ trở thành 387336856. Cảm ơn Gotkhan Kurt, người đã chỉ ra điều này.


Xem docs.microsoft.com/en-us/dotnet/api/ trên phần nhận xét. "Mã băm cho các chuỗi giống hệt nhau có thể khác nhau giữa các triển khai .NET, trên các phiên bản .NET và trên các nền tảng .NET (như 32 bit và 64 bit) cho một phiên bản .NET. Trong một số trường hợp, chúng thậm chí có thể khác nhau theo miền ứng dụng "
Liên kết

@Link có liên quan như thế nào? đó thậm chí không phải là một chuỗi, tài sản là một int.
Sedat Kapanoglu

[HashCode] .Combine?
Ry-

Xin lỗi liên kết sai: docs.microsoft.com/en-us/dotnet/api/ trộm Hành vi này cũng áp dụng cho Object.GetHashcode @SedatKapanoglu
Liên kết

2
-1937169414là phép nhân số nguyên của -1521134295-783812246. Con số quan trọng hơn ở đây là -1521134295xuất hiện trong mọi tính toán mã băm. -783812246là số hạt giống. Một số hạt giống được chọn dựa trên số lượng thành viên trong phương trình. Trong các lớp ẩn danh, số hạt giống được tính dựa trên tên trường. Vì vậy, có nhiều số hạt giống như có số nguyên. Chúng ta có thể giả sử một số hạt giống là ngẫu nhiên. Về tầm quan trọng của -1521134295, tôi nghĩ rằng nó làm giảm sự va chạm và chỉ có một nhà phát triển bên trong mới có thể trả lời chính xác làm thế nào.
Gotkhan Kurt

Câu trả lời:


2

Nếu bạn tìm kiếm -1521134295trong kho của Microsoft, bạn sẽ thấy rằng nó xuất hiện khá nhiều lần

Hầu hết các kết quả tìm kiếm đều nằm trong các GetHashCodehàm, nhưng tất cả chúng đều có dạng sau

int hashCode = SOME_CONSTANT;
hashCode = hashCode * -1521134295 + field1.GetHashCode();
hashCode = hashCode * -1521134295 + field2.GetHashCode();
// ...
return hashCode;

Cái đầu tiên hashCode * -1521134295 = SOME_CONSTANT * -1521134295sẽ được nhân lên trước trong thời gian tạo bởi trình tạo hoặc trong thời gian biên dịch bởi CSC. Đó là lý do -1937169414trong mã của bạn

Tìm hiểu sâu hơn về các kết quả cho thấy phần tạo mã có thể được tìm thấy trong hàm CreateGetHashCodeMethodStatements

const int hashFactor = -1521134295;

var initHash = 0;
var baseHashCode = GetBaseGetHashCodeMethod(containingType);
if (baseHashCode != null)
{
    initHash = initHash * hashFactor + Hash.GetFNVHashCode(baseHashCode.Name);
}

foreach (var symbol in members)
{
    initHash = initHash * hashFactor + Hash.GetFNVHashCode(symbol.Name);
}

Như bạn có thể thấy băm phụ thuộc vào tên biểu tượng. Trong hàm đó, hằng số cũng được gọi permuteValue, có lẽ bởi vì sau khi nhân, các bit được hoán vị xung quanh bằng cách nào đó

// -1521134295
var permuteValue = CreateLiteralExpression(factory, hashFactor);

Có một số mẫu nếu chúng ta xem giá trị ở dạng nhị phân: 101001 010101010101010 101001 01001hoặc 10100 1010101010101010 10100 10100 1. Nhưng nếu chúng ta nhân một giá trị tùy ý với giá trị đó thì có rất nhiều giá trị chồng chéo để tôi không thể thấy nó hoạt động như thế nào. Đầu ra cũng có thể có số bit thiết lập khác nhau nên nó không thực sự là một hoán vị

Bạn có thể tìm thấy một trình tạo khác trong AnonymousTypeGetHashCodeMethodSymbol của Roslyn , gọi hằng sốHASH_FACTOR

//  Method body:
//
//  HASH_FACTOR = 0xa5555529;
//  INIT_HASH = (...((0 * HASH_FACTOR) + GetFNVHashCode(backingFld_1.Name)) * HASH_FACTOR
//                                     + GetFNVHashCode(backingFld_2.Name)) * HASH_FACTOR
//                                     + ...
//                                     + GetFNVHashCode(backingFld_N.Name)

Lý do thực sự để chọn giá trị đó vẫn chưa rõ ràng


Đây là nghiên cứu tuyệt vời, cảm ơn bạn. Tôi không biết việc tạo mã băm là ở Roslyn, tôi nghĩ đó sẽ là Visual Studio.
Sedat Kapanoglu

3

Như GotkhanKurt đã giải thích trong các bình luận, số lượng thay đổi dựa trên tên tài sản liên quan. Nếu bạn đổi tên tài sản thành Halue, số sẽ trở thành 387336856. Tôi đã thử nó với các lớp khác nhau nhưng không nghĩ đến việc đổi tên tài sản.

Nhận xét của Gotkhan khiến tôi hiểu mục đích của nó. Nó bù đắp các giá trị băm dựa trên phần bù xác định nhưng được phân phối ngẫu nhiên. Bằng cách này, kết hợp các giá trị băm cho các lớp khác nhau, ngay cả với một bổ sung đơn giản, vẫn có khả năng chống lại các va chạm băm.

Chẳng hạn, nếu bạn có hai lớp với các triển khai GetHashCode tương tự:

public class A
{
    public int Value { get; set;}
    public int GetHashCode() => Value;
}

public class B
{
    public int Value { get; set;}
    public override int GetHashCode() => Value;
}

và nếu bạn có một lớp khác có chứa các tham chiếu đến hai:

public class C
{
    public A ValueA { get; set; }
    public B ValueB { get; set; }
    public override int GetHashCode()
    {
        return ValueA.GetHashCode() + ValueB.GetHashCode();
    }
}

một kết hợp kém như thế này sẽ dễ bị va chạm băm vì mã băm kết quả sẽ tích lũy xung quanh cùng một khu vực cho các giá trị khác nhau của ValueA và ValueB nếu các giá trị của chúng gần nhau. Sẽ không có vấn đề gì nếu bạn sử dụng các phép toán nhân hoặc bitwise để kết hợp chúng, chúng vẫn có thể bị va chạm mà không có độ lệch đồng đều. Vì nhiều giá trị số nguyên được sử dụng trong lập trình được tích lũy quanh 0, nên sử dụng phần bù như vậy là hợp lý

Rõ ràng, đó là một thực hành tốt để có một phần bù ngẫu nhiên với các mẫu bit tốt.

Tôi vẫn không chắc tại sao họ không sử dụng bù trừ hoàn toàn ngẫu nhiên, có lẽ không phá vỡ bất kỳ mã nào dựa trên tính xác định của GetHashCode (), nhưng thật tuyệt khi nhận được nhận xét từ nhóm Visual Studio về điều này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.