هل يمكنني الاعتماد على قيم GetHashCode() لتكون متسقة؟

StackOverflow https://stackoverflow.com/questions/53086

  •  09-06-2019
  •  | 
  •  

سؤال

هل القيمة المرجعة لـ GetHashCode() مضمونة لتكون متسقة بافتراض استخدام نفس قيمة السلسلة؟(C#/ASP.NET)

لقد قمت بتحميل الكود الخاص بي إلى خادم اليوم ولدهشتي اضطررت إلى إعادة فهرسة بعض البيانات لأن الخادم الخاص بي (win2008 64 بت) كان يعرض قيمًا مختلفة مقارنة بجهاز الكمبيوتر المكتبي الخاص بي.

هل كانت مفيدة؟

المحلول

إذا لم أكن مخطئًا، فإن GetHashCode متسق بالنظر إلى نفس القيمة، ولكن ليس من المضمون أن يكون متسقًا عبر الإصدارات المختلفة من إطار العمل.

من مستندات MSDN على String.GetHashCode():

يعتمد سلوك GetHashCode على تنفيذه، والذي قد يتغير من إصدار واحد من وقت تشغيل اللغة العامة إلى آخر.أحد أسباب حدوث ذلك هو تحسين أداء GetHashCode.

نصائح أخرى

واجهت مشكلة مماثلة حيث قمت بملء جدول قاعدة البيانات بالمعلومات التي كانت تعتمد على String.GetHashCode (ليست أفضل فكرة) وعندما قمت بترقية الخادم الذي كنت أعمل عليه إلى x64 لاحظت أن القيم التي كنت أحصل عليها من String.GetHashCode كانت يتعارض مع ما كان موجودًا بالفعل في الجدول.كان الحل الذي توصلت إليه هو استخدام الإصدار الخاص بي من GetHashCode والذي يُرجع نفس قيمة String.GetHashCode على إطار عمل x86.

إليك الكود، لا تنس تجميعه باستخدام "السماح بالكود غير الآمن":

    /// <summary>
    /// Similar to String.GetHashCode but returns the same as the x86 version of String.GetHashCode for x64 and x86 frameworks.
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static unsafe int GetHashCode32(string s)
    {
        fixed (char* str = s.ToCharArray())
        {
            char* chPtr = str;
            int num = 0x15051505;
            int num2 = num;
            int* numPtr = (int*)chPtr;
            for (int i = s.Length; i > 0; i -= 4)
            {
                num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
                if (i <= 2)
                {
                    break;
                }
                num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
                numPtr += 2;
            }
            return (num + (num2 * 0x5d588b65));
        }
    }

يعتمد التنفيذ على إصدار إطار العمل ولكنه يعتمد أيضًا على بنيان.يختلف تنفيذ string.GetHashCode() في الإصدارين x86 وx64 من إطار العمل حتى لو كان لهما نفس رقم الإصدار.

أتساءل عما إذا كانت هناك اختلافات بين أنظمة التشغيل 32 بت و64 بت، لأنني متأكد من أن الخادم والكمبيوتر المنزلي يعملان بنفس الإصدار من .NET

لقد كنت دائمًا سئمت من استخدام GetHashCode()، وقد تكون فكرة جيدة بالنسبة لي أن أقوم ببساطة بدور خوارزمية التجزئة الخاصة بي.حسنًا، على الأقل انتهى بي الأمر إلى كتابة صفحة aspx. لإعادة الفهرسة السريعة بسبب ذلك.

هل تقوم بتشغيل Win2008 x86 كسطح المكتب الخاص بك؟لأن Win2008 يتضمن الإصدار 2.0.50727.1434, ، وهو إصدار محدث من 2.0 مضمن في Vista RTM.

ومع ذلك ، فإن ما لاحظناه ، عندما يكون كائن في كائن جمع التجزئة (علامة تصنيف ، قاموس ، إلخ) ، عندما لا يكون كائنان فريدان ولكنهما رموزهم ، يتم استخدام Hashcode فقط كبحث أول خيار ، إذا كان هناك -رموز التجزئة التي يتم استخدامها ، يتم استخدام مشغل المساواة دائمًا كقعدة لاستخلاص المساواة.

هذه هي الطريقة التي تعمل بها عمليات البحث عن التجزئة، أليس كذلك؟تحتوي كل مجموعة على قائمة بالعناصر التي لها نفس رمز التجزئة.

لذلك، للعثور على العنصر الصحيح في ظل هذه الظروف، يتم إجراء بحث خطي باستخدام مقارنة مساواة القيمة.

وإذا حقق تنفيذ التجزئة توزيعًا جيدًا، فلن يكون هذا البحث مطلوبًا، أي عنصر واحد لكل مجموعة.

فهل فهمي صحيح؟

ليست إجابة مباشرة على سؤالك، والتي أجاب عليها جوناس بشكل جيد، ولكن قد يكون هذا مفيدًا إذا كنت قلقًا بشأن اختبار المساواة في التجزئات

من خلال اختباراتنا، اعتمادًا على ما تحتاجه من رموز التجزئة، في C#، لا يلزم أن تكون رموز التجزئة فريدة لعمليات المساواة.على سبيل المثال، خذ بعين الاعتبار ما يلي:

كان لدينا مطلب لزيادة التحميل على عامل يساوي، وبالتالي وظيفة GetHashCode للكائنات لدينا لأنها أصبحت متطايرة وعديمة الحالة، وتستمد نفسها مباشرة من البيانات، لذلك في مكان واحد من التطبيق كنا بحاجة إلى التأكد من أنه سيتم عرض الكائن على قدم المساواة مع كائن آخر إذا كان مصدرها نفس البيانات, ، وليس فقط إذا كان نفس المرجع.معرفات البيانات الفريدة لدينا هي أدلة.

كان من السهل تلبية عامل يساوي حيث قمنا للتو بالتحقق من دليل السجل (بعد التحقق من وجود قيمة فارغة).

لسوء الحظ، يعتمد حجم بيانات HashCode (كونه int) على نظام التشغيل، وعلى نظامنا 32 بت، سيكون رمز التجزئة 32 بت.رياضيًا، عندما نتجاوز وظيفة GetHashCode، فمن المستحيل إنشاء رمز تجزئة فريد من دليل أكبر من 32 بت (انظر إليه من العكس، كيف يمكنك ترجمة عدد صحيح 32 بت إلى دليل إرشادي؟).

أجرينا بعد ذلك بعض الاختبارات حيث أخذنا الدليل كسلسلة وأعدنا رمز التجزئة الخاص بالمرشد، والذي يُرجع دائمًا معرفًا فريدًا في اختباراتنا، ولكن ليس دائمًا.

ومع ذلك، ما لاحظناه، عندما يكون الكائن في كائن مجموعة مجزأة (جدول تجزئة، قاموس، إلخ)، عندما لا يكون هناك كائنان فريدان ولكن رموز التجزئة الخاصة بهما فريدة، يتم استخدام رمز التجزئة فقط كخيار بحث أول، إذا لم يكن هناك -رموز التجزئة الفريدة المستخدمة، يُستخدم عامل المساواة دائمًا كأداة احتياطية لتحديد المساواة.

كما قلت، قد يكون هذا أو لا يكون ذا صلة بموقفك، ولكن إذا كان الأمر كذلك، فهذه نصيحة مفيدة.

تحديث

للتوضيح، لدينا Hashtable:

المفتاح: الكائن A (رمز التجزئة 1)، قيمة الكائن A1

المفتاح: الكائن B (رمز التجزئة 1)، قيمة الكائن B1

المفتاح: الكائن C (رمز التجزئة 1)، قيمة الكائن C1

المفتاح: الكائن D (رمز التجزئة 2)، قيمة الكائن D1

المفتاح: الكائن E (رمز التجزئة 3)، قيمة الكائن E1

عندما أقوم باستدعاء جدول التجزئة للكائن باستخدام مفتاح الكائن A، سيتم إرجاع الكائن A1 بعد خطوتين، واستدعاء رمز التجزئة 1، ثم التحقق من المساواة على كائن المفتاح حيث لا يوجد مفتاح فريد مع رمز التجزئة 1

عندما أقوم باستدعاء جدول التجزئة للكائن باستخدام مفتاح الكائن D، سيتم إرجاع الكائن D1 بعد خطوة واحدة، بحث عن التجزئة

    /// <summary>
    /// Default implementation of string.GetHashCode is not consistent on different platforms (x32/x64 which is our case) and frameworks. 
    /// FNV-1a - (Fowler/Noll/Vo) is a fast, consistent, non-cryptographic hash algorithm with good dispersion. (see http://isthe.com/chongo/tech/comp/fnv/#FNV-1a)
    /// </summary>
    private static int GetFNV1aHashCode(string str)
    {
        if (str == null)
            return 0;
        var length = str.Length;
        // original FNV-1a has 32 bit offset_basis = 2166136261 but length gives a bit better dispersion (2%) for our case where all the strings are equal length, for example: "3EC0FFFF01ECD9C4001B01E2A707"
        int hash = length;
        for (int i = 0; i != length; ++i)
            hash = (hash ^ str[i]) * 16777619;
        return hash;
    }

يمكن أن يكون هذا التنفيذ أبطأ من التطبيق غير الآمن الذي تم نشره من قبل.ولكن أبسط بكثير وآمنة.

أود أن أقول... لا يمكنك الاعتماد عليه.على سبيل المثال، إذا قمت بتشغيل file1 من خلال رمز التجزئة md5 الخاص بـ c# وقمت بنسخ نفس الملف ولصقه في دليل جديد... فإن رمز التجزئة يظهر مختلفًا حتى أنه صعب فهو نفس الملف.من الواضح أنه نفس الإصدار .net، نفس كل شيء.الشيء الوحيد الذي تغير هو المسار.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top