গেটহ্যাশকোডকে ওভাররাইড করার জন্য সেরা অ্যালগরিদম কী?


1448

.NET- এ, GetHashCodeপদ্ধতিটি NET বেস শ্রেণীর পাঠাগারগুলিতে প্রচুর জায়গায় ব্যবহৃত হয় in এটিকে যথাযথভাবে প্রয়োগ করা বিশেষত কোনও সংগ্রহের মধ্যে বা সমতা নির্ধারণের সময় আইটেমগুলি সন্ধান করা বিশেষত গুরুত্বপূর্ণ।

GetHashCodeআমার কাস্টম ক্লাসগুলির জন্য কীভাবে প্রয়োগ করা যায় সে সম্পর্কে কোনও মানক অ্যালগরিদম বা সেরা অনুশীলন রয়েছে যাতে আমি কর্মক্ষমতা হ্রাস না করি?


38
এই প্রশ্ন এবং নীচের নিবন্ধটি পড়ার পরে, আমি ওভাররাইড বাস্তবায়ন করতে পারি GetHashCode। আমি আশা করি এটি অন্যদের জন্য সহায়ক হবে। নির্দেশিকা এবং GetHashCode জন্য নিয়ম এরিক Lippert দ্বারা লিখিত
Rene

4
"বা সমতা নির্ধারণ করতে": না! একই হ্যাশকোডযুক্ত দুটি বস্তু অগত্যা সমান নয়।
টমাস লেভেস্ক

1
@ থমাস লেভসেক আপনি ঠিক বলেছেন, একই হ্যাশ কোডযুক্ত দুটি বস্তু অগত্যা সমান নয়। কিন্তু এখনও এর GetHashCode()অনেক বাস্তবায়নে ব্যবহৃত হয় Equals()। আমি এই বিবৃতি দিয়ে বোঝাতে চেয়েছিলাম। প্রায়শই GetHashCode()ভিতরে বৈষম্যEquals() নির্ধারণের জন্য শর্টকাট হিসাবে ব্যবহৃত হয় , কারণ দুটি বস্তুর যদি আলাদা হ্যাশ কোড থাকে তবে তাদের এমন বস্তু হতে হবে যা সমান নয় এবং বাকী সাম্যতা-চেকটি কার্যকর করতে হবে না।
বিটবঙ্ক

3
@ বিটবোনক সাধারণত, উভয় GetHashCode()এবং Equals()উভয় বস্তুর সমস্ত ক্ষেত্রের দিকে নজর দেওয়া প্রয়োজন (হ্যাশকোডগুলি সমান বা না-পরীক্ষা করা হলে সমান এটি করতে হবে)। এ কারণে, GetHashCode()ভিতরে ভিতরে কল করা Equals()প্রায়শই অতিরিক্ত হয় এবং পারফরম্যান্স হ্রাস করতে পারে। Equals()এটি আরও দ্রুত তৈরি করে শর্ট সার্কিট করতে সক্ষম হতে পারে - তবে কিছু ক্ষেত্রে হ্যাশকোডগুলি ক্যাশে করা যেতে পারে যা চেকটিকে GetHashCode()দ্রুত এবং ততটা সার্থক করে তোলে । আরও জানতে এই প্রশ্ন দেখুন।
নোটইনটাডাটা

উত্তর:


1602

আমি সাধারণত জোশ ব্লচের কল্পিত কার্যকর জাভাতে প্রদত্ত বাস্তবায়নের মতো কিছু নিয়ে যাই । এটি দ্রুত এবং একটি দুর্দান্ত ভাল হ্যাশ তৈরি করে যা সংঘর্ষের সম্ভাবনা কম is দুটি পৃথক মৌলিক সংখ্যা বাছাই করুন, যেমন 17 এবং 23, এবং করুন:

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 17;
        // Suitable nullity checks etc, of course :)
        hash = hash * 23 + field1.GetHashCode();
        hash = hash * 23 + field2.GetHashCode();
        hash = hash * 23 + field3.GetHashCode();
        return hash;
    }
}

মন্তব্যে উল্লিখিত হিসাবে, আপনি তার পরিবর্তে গুন করার জন্য একটি বৃহত প্রধান চয়ন করা ভাল। স্পষ্টতই 486187739 ভাল ... এবং যদিও আমি বেশিরভাগ উদাহরণে ছোট সংখ্যার সাথে দেখেছি প্রাইম ব্যবহার করার ঝোঁক রয়েছে, কমপক্ষে অনুরূপ অ্যালগরিদম রয়েছে যেখানে অ-মৌলিক সংখ্যা প্রায়শই ব্যবহৃত হয়। পরে উল্লেখযোগ্য- এফএনভি উদাহরণে উদাহরণস্বরূপ, আমি সংখ্যাগুলি ব্যবহার করেছি যা দৃশ্যত ভালভাবে কাজ করে - তবে প্রাথমিক মানটি প্রধান নয়। (গুণ ধ্রুবক হয় মৌলিক যদিও। আমি বেশ কিভাবে গুরুত্বপূর্ণ যে জানি না।)

এটি XORদুটি প্রধান কারণে হ্যাশকোডগুলি ইঙ্গিত করার সাধারণ অনুশীলনের চেয়ে ভাল better ধরুন আমাদের দুটি intক্ষেত্রের সাথে একটি প্রকার রয়েছে:

XorHash(x, x) == XorHash(y, y) == 0 for all x, y
XorHash(x, y) == XorHash(y, x) for all x, y

যাইহোক, পূর্ববর্তী অ্যালগরিদমটি বর্তমানে বেনামি ধরণের জন্য সি # সংকলক ব্যবহার করে।

এই পৃষ্ঠাটি বেশ কয়েকটি বিকল্প দেয়। আমি মনে করি উপরের বেশিরভাগ ক্ষেত্রে "যথেষ্ট ভাল" এবং এটি সঠিকভাবে মনে রাখা এবং সঠিকভাবে পাওয়া অসম্ভব সহজ। FNV বিকল্প একভাবে সহজ, কিন্তু বিভিন্ন ধ্রুবক এবং ব্যবহার XORপরিবর্তে ADDএকটি মিশ্রন অপারেশন হিসাবে। এটি নীচের কোডটির মতো কিছু দেখাচ্ছে , তবে সাধারণ এফএনভি অ্যালগরিদম পৃথক বাইটগুলিতে পরিচালনা করে, সুতরাং প্রতি 32-বিট হ্যাশ মানের পরিবর্তে প্রতি বাইটে একটি পুনরাবৃত্তি সম্পাদন করার জন্য এটির সংশোধন করা দরকার। এফএনভিও পরিবর্তনশীল দৈর্ঘ্যের ডেটার জন্য ডিজাইন করা হয়েছে, যেখানে আমরা এখানে এটি যেভাবে ব্যবহার করছি তা সর্বদা একই সংখ্যার ক্ষেত্রের মানগুলির জন্য। এই উত্তরের মন্তব্যগুলি পরামর্শ দেয় যে এখানে কোডটি আসলে উপরের সংযোজন পদ্ধতির হিসাবে (পরীক্ষিত নমুনা ক্ষেত্রে) পাশাপাশি কাজ করে না।

// Note: Not quite FNV!
public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = (int) 2166136261;
        // Suitable nullity checks etc, of course :)
        hash = (hash * 16777619) ^ field1.GetHashCode();
        hash = (hash * 16777619) ^ field2.GetHashCode();
        hash = (hash * 16777619) ^ field3.GetHashCode();
        return hash;
    }
}

মনে রাখবেন যে একটি বিষয় সম্পর্কে সচেতন থাকতে হবে তা হ্যাশ কোডের উপর নির্ভরশীল কোনও সংকলনে যোগ করার পরে আদর্শভাবে আপনার সমতা-সংবেদনশীল (এবং এইভাবে হ্যাশকোড-সংবেদনশীল) রাষ্ট্র পরিবর্তন করা থেকে বিরত রাখা উচিত।

অনুযায়ী ডকুমেন্টেশন :

অপরিবর্তনীয় রেফারেন্স ধরণের জন্য আপনি getHashCode ওভাররাইড করতে পারেন। সাধারণভাবে, পরিবর্তনীয় রেফারেন্স ধরণের জন্য, আপনার কেবলমাত্র গেটহ্যাশকোডকে ওভাররাইড করা উচিত যদি:

  • আপনি এমন ক্ষেত্রগুলি থেকে হ্যাশ কোডটি গণনা করতে পারেন যা পরিবর্তনীয় নয়; অথবা
  • আপনি নিশ্চিত করতে পারেন যে কোনও হ'ল কোডের উপর নির্ভর করে এমন কোনও সংস্থায় বস্তুটি থাকা অবস্থায় কোনও মিউটয়েবল অবজেক্টের হ্যাশ কোডটি পরিবর্তন হয় না।

8
আপনি যে বইয়ের উল্লেখ করেছেন তাতে বর্ণিত অ্যালগরিদমটি আরও কিছুটা বিশদভাবে স্পষ্টভাবে বর্ণনা করা হয়েছে ক্ষেত্রগুলির বিভিন্ন ডেটা ধরণের জন্য কী করা উচিত তা স্পষ্ট করে বর্ণনা করে। উদাহরণস্বরূপ: কেবলমাত্র গেটহ্যাশকোড কল করার পরিবর্তে দীর্ঘ ব্যবহারের (ক্ষেত্রের int চ >>> 32) প্রকারের ক্ষেত্রে। দীর্ঘ.গেটহ্যাশকোডস কি সেভাবে কার্যকর হয়েছে?
বিটবঙ্ক

13
হ্যাঁ, Int64.GetHashCode ঠিক এটি করে। জাভাতে অবশ্যই বক্সিং প্রয়োজন হবে। এটি আমাকে স্মরণ করিয়ে দেয় - বইটিতে একটি লিঙ্ক যুক্ত করার সময় ...
জন স্কিটি

77
23 কোনও ভাল পছন্দ নয়, যেহেতু (.net 3.5 এসপি 1 হিসাবে) Dictionary<TKey,TValue>ভাল বিতরণটি নির্দিষ্ট কিছু প্রাইমুলকে ধরে নিয়েছে। এবং 23 তাদের মধ্যে একটি। সুতরাং আপনার যদি ক্যাপাসিটি 23 সহ একটি অভিধান থাকে তবে কেবল GetHashCodeযৌগিক হ্যাশকোডকে প্রভাবিত করতে সর্বশেষ অবদান । সুতরাং আমি পরিবর্তে ২৩ এর পরিবর্তে ২৯ ব্যবহার করব
কোডসইনচাউস

23
@ কোডইনচায়োস: কেবলমাত্র শেষ অবদান বালতিকে প্রভাবিত করে - সুতরাং এটি সম্ভবত সবচেয়ে খারাপভাবে অভিধানে সমস্ত 23 টি এন্ট্রি সন্ধান করতে পারে। এটি এখনও প্রতিটি প্রবেশের আসল হ্যাশ কোডটি যাচাই করতে চলেছে, যা সস্তা হবে। যদি আপনার কাছে একটি ছোট্ট অভিধান থাকে তবে এটি খুব বেশি গুরুত্ব পাবে না।
জন স্কিটি

20
@ বাজদা: আমি সাধারণত কার্যকর হ্যাশ কোড হিসাবে 0 ব্যবহার করি null- যা ক্ষেত্রটিকে উপেক্ষা করার মতো নয়।
জন স্কিটি

431

নামবিহীন প্রকার

মাইক্রোসফ্ট ইতিমধ্যে একটি ভাল জেনেরিক হ্যাশকোড জেনারেটর সরবরাহ করে: কেবল আপনার সম্পত্তি / ক্ষেত্রের মানগুলি একটি বেনামে অনুলিপি করুন এবং এটি হ্যাশ করুন:

new { PropA, PropB, PropC, PropD }.GetHashCode();

এটি যে কোনও সংখ্যক বৈশিষ্ট্যের জন্য কাজ করবে। এটি বক্সিং ব্যবহার করে না। এটি কেবল বেনামে প্রকারের জন্য ফ্রেমওয়ার্কে ইতিমধ্যে প্রয়োগ করা অ্যালগরিদম ব্যবহার করে।

মান মূল্য - সি # 7 এর জন্য আপডেট

@ ক্যাক্টুয়ারয়েড মন্তব্যগুলিতে উল্লেখ হিসাবে, একটি মান টিপল ব্যবহার করা যেতে পারে। এটি কয়েকটি কীস্ট্রোক সংরক্ষণ করে এবং আরও গুরুত্বপূর্ণভাবে স্ট্যাকের উপর খাঁটিভাবে নির্বাহ করে (কোনও আবর্জনা নেই):

(PropA, PropB, PropC, PropD).GetHashCode();

(দ্রষ্টব্য: বেনামে ধরণের ব্যবহার করে আসল কৌশলটি মনে হয় যে গবাদি পশুর উপর একটি বস্তু তৈরি হয়েছে, অর্থাৎ আবর্জনা, যেহেতু বেনামে প্রকারগুলি ক্লাস হিসাবে প্রয়োগ করা হয়, যদিও এটি সংকলক দ্বারা অপ্টিমাইজড হতে পারে these এই বিকল্পগুলির মানদণ্ডটি আকর্ষণীয় হবে তবে এটি টিপল বিকল্পটি সর্বোত্তম হওয়া উচিত))


85
হ্যাঁ, বেনামে GetHashCodeবাস্তবায়ন খুব কার্যকর (বিটিডাব্লু এটি জন স্কিটির উত্তরের মতোই) তবে এই সমাধানটির একমাত্র সমস্যাটি হ'ল আপনি যে কোনও GetHashCodeকলটিতে একটি নতুন উদাহরণ তৈরি করেন । বড় আকারের হ্যাশ সংগ্রহগুলিতে নিবিড় অ্যাক্সেসের ক্ষেত্রে এটি কিছুটা ওভারহেড-ইশ হতে পারে ...
digEmAll

5
@ ডিগেম সমস্ত ভাল কথা, আমি একটি নতুন অবজেক্ট তৈরি করার ওভারহেড সম্পর্কে ভাবি নি। জন স্কিটির উত্তর সর্বাধিক দক্ষ এবং বক্সিং ব্যবহার করবে না। (@ কুম্বা ভিবিতে চেক না করা সমাধানের জন্য, কেবল একটি ইন্টার 64 (দীর্ঘ) ব্যবহার করুন এবং গণনার পরে এটি কেটে দিন।)
রিক লাভ

42
শুধু বলতে পারে new { PropA, PropB, PropC, PropD }.GetHashCode()খুব
sehe

17
VB.NET অবশ্যই বেনামি প্রকারের তৈরিতে কী ব্যবহার করতে হবে: New With {Key PropA}.GetHashCode()অন্যথায় গেটহ্যাশকোড একই 'চিহ্নিতকরণ' বৈশিষ্ট্যযুক্ত বিভিন্ন বস্তুর জন্য একই হ্যাশকোডটি ফিরিয়ে দেবে না।
ডেভিড ওসবার্ন

4
@ কিথ, আমি এই ক্ষেত্রে প্রত্যেকবার হ্যাশকোড গণনা করার পরে গণনা করার পরিবর্তে আইইনুয়েবলকে একটি তালিকা মান হিসাবে সংরক্ষণ করার বিষয়টি বিবেচনা করব। গেটহ্যাশকোডের অভ্যন্তরে প্রতিবার টোললিস্টকে ক্যালকুলেটিং করা অনেক পরিস্থিতিতে পারফরম্যান্সকে আঘাত করতে পারে।
রিক লাভ

105

এখানে আমার হ্যাশকোড সহায়ক।
এটির সুবিধাটি হ'ল এটি জেনেরিক ধরণের আর্গুমেন্টগুলি ব্যবহার করে এবং তাই বক্সিংকে সৃষ্টি করবে না:

public static class HashHelper
{
    public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
    {
         unchecked
         {
             return 31 * arg1.GetHashCode() + arg2.GetHashCode();
         }
    }

    public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
    {
        unchecked
        {
            int hash = arg1.GetHashCode();
            hash = 31 * hash + arg2.GetHashCode();
            return 31 * hash + arg3.GetHashCode();
        }
    }

    public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, 
        T4 arg4)
    {
        unchecked
        {
            int hash = arg1.GetHashCode();
            hash = 31 * hash + arg2.GetHashCode();
            hash = 31 * hash + arg3.GetHashCode();
            return 31 * hash + arg4.GetHashCode();
        }
    }

    public static int GetHashCode<T>(T[] list)
    {
        unchecked
        {
            int hash = 0;
            foreach (var item in list)
            {
                hash = 31 * hash + item.GetHashCode();
            }
            return hash;
        }
    }

    public static int GetHashCode<T>(IEnumerable<T> list)
    {
        unchecked
        {
            int hash = 0;
            foreach (var item in list)
            {
                hash = 31 * hash + item.GetHashCode();
            }
            return hash;
        }
    }

    /// <summary>
    /// Gets a hashcode for a collection for that the order of items 
    /// does not matter.
    /// So {1, 2, 3} and {3, 2, 1} will get same hash code.
    /// </summary>
    public static int GetHashCodeForOrderNoMatterCollection<T>(
        IEnumerable<T> list)
    {
        unchecked
        {
            int hash = 0;
            int count = 0;
            foreach (var item in list)
            {
                hash += item.GetHashCode();
                count++;
            }
            return 31 * hash + count.GetHashCode();
        }
    }

    /// <summary>
    /// Alternative way to get a hashcode is to use a fluent 
    /// interface like this:<br />
    /// return 0.CombineHashCode(field1).CombineHashCode(field2).
    ///     CombineHashCode(field3);
    /// </summary>
    public static int CombineHashCode<T>(this int hashCode, T arg)
    {
        unchecked
        {
            return 31 * hashCode + arg.GetHashCode();   
        }
    }

এছাড়াও এটি একটি সাবলীল ইন্টারফেস সরবরাহ করার জন্য এক্সটেনশন পদ্ধতি রয়েছে, আপনি এটি এটি ব্যবহার করতে পারেন:

public override int GetHashCode()
{
    return HashHelper.GetHashCode(Manufacturer, PartN, Quantity);
}

বা এই মত:

public override int GetHashCode()
{
    return 0.CombineHashCode(Manufacturer)
        .CombineHashCode(PartN)
        .CombineHashCode(Quantity);
}

5
T[]এটি ইতিমধ্যে আলাদাভাবে করার দরকার নেইIEnumerable<T>
নওফাল

5
আপনি এই পদ্ধতিগুলিকে
রিফেক্টর

12
ঘটনাচক্রে, 31 হ'ল সিপিইউতে একটি শিফট এবং বিয়োগ, যা অত্যন্ত দ্রুত ingly
চুই

4
@nightcoder আপনি ব্যবহার করতে পারে প্যারাম
এভিনিস

6
@ChuiTey এইটি এমন কিছু বিষয় সব Mersenne primes কমন আছে।
ফারাপ

63

হেল্পার লাইব্রেরিতে আমার একটি হ্যাশিং ক্লাস রয়েছে যা আমি এই উদ্দেশ্যে এটি ব্যবহার করি।

/// <summary> 
/// This is a simple hashing function from Robert Sedgwicks Hashing in C book.
/// Also, some simple optimizations to the algorithm in order to speed up
/// its hashing process have been added. from: www.partow.net
/// </summary>
/// <param name="input">array of objects, parameters combination that you need
/// to get a unique hash code for them</param>
/// <returns>Hash code</returns>
public static int RSHash(params object[] input)
{
    const int b = 378551;
    int a = 63689;
    int hash = 0;

    // If it overflows then just wrap around
    unchecked
    {
        for (int i = 0; i < input.Length; i++)
        {
            if (input[i] != null)
            {
                hash = hash * a + input[i].GetHashCode();
                a = a * b;
            }
        }
    }

    return hash;
}

তারপরে, আপনি কেবল এটিকে ব্যবহার করতে পারেন:

public override int GetHashCode()
{
    return Hashing.RSHash(_field1, _field2, _field3);
}

আমি এর কার্যকারিতা মূল্যায়ন করিনি, সুতরাং কোনও প্রতিক্রিয়া স্বাগত জানানো হয়।


26
ঠিক আছে, ক্ষেত্রগুলি মান ধরণের হলে এটি বক্সিংয়ের কারণ ঘটবে।
নাইটকোডার

5
"ওভারফ্লো এক্সেকশনটি ধরে রেখে উন্নত করা যেতে পারে" এর পুরো পয়েন্টটি uncheckedহ'ল ওভারফ্লোতে ব্যতিক্রমগুলি এড়ানো যা পছন্দসই GetHashCode। সুতরাং যদি মানটি ওভারফ্লো হয়ে যায় intএবং এটি কোনওরকম আঘাত না করে তবে এটি ভুল নয়।
টিম শ্মেলটার

1
এই অ্যালগরিদমের সাথে একটি সমস্যা হ'ল নলগুলি পূর্ণ কোনও অ্যারে সর্বদা 0 ফিরে আসবে, তার দৈর্ঘ্য নির্বিশেষে
নাথান অ্যাডামস

2
এই সহায়ক পদ্ধতিটি একটি নতুন অবজেক্টকেও বরাদ্দ করে []
জেমস নিউটন-কিং

1
@ নাথানএডামস যেমন উল্লেখ করেছেন, nullপুরোপুরি এড়িয়ে যাওয়ার বিষয়টি আপনাকে অপ্রত্যাশিত ফলাফল দিতে পারে। এগুলি এড়িয়ে যাওয়ার পরিবর্তে আপনার শূন্য input[i].GetHashCode()হওয়ার পরিবর্তে কিছু ধ্রুবক মান ব্যবহার করা উচিত input[i]
ডেভিড শোয়ার্টজ

58

জন স্কিটির বাস্তবায়নটি ব্যবহার করে আমার সহায়ক শ্রেণিটি এখানে ।

public static class HashCode
{
    public const int Start = 17;

    public static int Hash<T>(this int hash, T obj)
    {
        var h = EqualityComparer<T>.Default.GetHashCode(obj);
        return unchecked((hash * 31) + h);
    }
}

ব্যবহার:

public override int GetHashCode()
{
    return HashCode.Start
        .Hash(_field1)
        .Hash(_field2)
        .Hash(_field3);
}

আপনি যদি সিস্টেমের জন্য একটি এক্সটেনশন পদ্ধতি লেখা এড়াতে চান তবে ইন্ট 32:

public readonly struct HashCode
{
    private readonly int _value;

    public HashCode(int value) => _value = value;

    public static HashCode Start { get; } = new HashCode(17);

    public static implicit operator int(HashCode hash) => hash._value;

    public HashCode Hash<T>(T obj)
    {
        var h = EqualityComparer<T>.Default.GetHashCode(obj);
        return unchecked(new HashCode((_value * 31) + h));
    }

    public override int GetHashCode() => _value;
}

এটি এখনও কোনও গাদা বরাদ্দ এড়ানো এবং ঠিক একইভাবে ব্যবহৃত হয়:

public override int GetHashCode()
{
    // This time `HashCode.Start` is not an `Int32`, it's a `HashCode` instance.
    // And the result is implicitly converted to `Int32`.
    return HashCode.Start
        .Hash(_field1)
        .Hash(_field2)     
        .Hash(_field3);
}

সম্পাদনা করুন (মে 2018): EqualityComparer<T>.Defaultসংগ্রহকারী এখন একটি জে আই টি JIT স্বকীয় হয় - খিঁচ অনুরোধ মধ্যে স্টিফেন Toub দ্বারা উল্লেখ করা হয় এই ব্লগ পোস্টে


1
আমি তৃতীয় অপারেটরের সাথে লাইনটি পরিবর্তন করব:var h = Equals(obj, default(T)) ? 0 : obj.GetHashCode();
বিল ব্যারি

আমি বিশ্বাস করি যে এর সাথে টের্নারি অপারেটর obj != nullএমন একটি boxনির্দেশকে সংকলন করবে যা Tমান ধরণের হলে মেমরির বরাদ্দ করবে । পরিবর্তে আপনি ব্যবহার করতে পারেন obj.Equals(null)যা Equalsপদ্ধতির ভার্চুয়াল কলটিতে সংকলন করবে ।
মার্টিন লিভারেজ

কারণ this.hashCode != h। এটি একই মান ফেরত দেবে না।
akফাক গুর

দুঃখিত, আমার মন্তব্য সম্পাদনা করার পরিবর্তে অপসারণ পরিচালনা করুন। নতুন স্ট্রাক তৈরি করা কি আরও বেশি উপকারী তা হলে হ্যাশকোডকে অ-পঠনযোগ্যতে পরিবর্তন করুন এবং করুন: "চেক না করা {this.hashCode h = h * 397;} এটি ফেরান;" উদাহরণ স্বরূপ?
এরিক কার্লসন

অপরিচ্ছন্নতার এর সুবিধাগুলি রয়েছে ( কেন পারস্পরিক পরিবর্তনগুলি খারাপ? )। পারফরম্যান্স সম্পর্কে, আমি যা করি তা বেশ সস্তা কারণ এটি গাদা কোনও স্থান বরাদ্দ করে না।
akফাক গুর

30

.NET স্ট্যান্ডার্ড 2.1 এবং উপরে

যদি আপনি .NET স্ট্যান্ডার্ড ২.১ বা তার বেশি ব্যবহার করেন তবে আপনি System.HashCode স্ট্রাক্ট ব্যবহার করতে পারেন । এটি ব্যবহারের দুটি পদ্ধতি রয়েছে:

HashCode.Combine

Combineপদ্ধতি, একটি হ্যাশ কোড তৈরি করতে ব্যবহার করা যাবে আট বস্তু প্রদর্শন করেছে।

public override int GetHashCode() => HashCode.Combine(this.object1, this.object2);

HashCode.Add

Addপদ্ধতি সংগ্রহের সঙ্গে মোকাবিলা করার জন্য আপনাকে সাহায্য করে:

public override int GetHashCode()
{
    var hashCode = new HashCode();
    hashCode.Add(this.object1);
    foreach (var item in this.collection)
    {
        hashCode.Add(item);
    }
    return hashCode.ToHashCode();
}

গেটহ্যাশকোড তৈরি করা সহজ

আরও বিশদ এবং মন্তব্যের জন্য আপনি সম্পূর্ণ ব্লগ পোস্ট ' গেটহ্যাশকোড তৈরি সহজ " পড়তে পারেন ।

ব্যবহারের উদাহরণ

public class SuperHero
{
    public int Age { get; set; }
    public string Name { get; set; }
    public List<string> Powers { get; set; }

    public override int GetHashCode() =>
        HashCode.Of(this.Name).And(this.Age).AndEach(this.Powers);
}

বাস্তবায়ন

public struct HashCode : IEquatable<HashCode>
{
    private const int EmptyCollectionPrimeNumber = 19;
    private readonly int value;

    private HashCode(int value) => this.value = value;

    public static implicit operator int(HashCode hashCode) => hashCode.value;

    public static bool operator ==(HashCode left, HashCode right) => left.Equals(right);

    public static bool operator !=(HashCode left, HashCode right) => !(left == right);

    public static HashCode Of<T>(T item) => new HashCode(GetHashCode(item));

    public static HashCode OfEach<T>(IEnumerable<T> items) =>
        items == null ? new HashCode(0) : new HashCode(GetHashCode(items, 0));

    public HashCode And<T>(T item) => 
        new HashCode(CombineHashCodes(this.value, GetHashCode(item)));

    public HashCode AndEach<T>(IEnumerable<T> items)
    {
        if (items == null)
        {
            return new HashCode(this.value);
        }

        return new HashCode(GetHashCode(items, this.value));
    }

    public bool Equals(HashCode other) => this.value.Equals(other.value);

    public override bool Equals(object obj)
    {
        if (obj is HashCode)
        {
            return this.Equals((HashCode)obj);
        }

        return false;
    }

    public override int GetHashCode() => this.value.GetHashCode();

    private static int CombineHashCodes(int h1, int h2)
    {
        unchecked
        {
            // Code copied from System.Tuple a good way to combine hashes.
            return ((h1 << 5) + h1) ^ h2;
        }
    }

    private static int GetHashCode<T>(T item) => item?.GetHashCode() ?? 0;

    private static int GetHashCode<T>(IEnumerable<T> items, int startHashCode)
    {
        var temp = startHashCode;

        var enumerator = items.GetEnumerator();
        if (enumerator.MoveNext())
        {
            temp = CombineHashCodes(temp, GetHashCode(enumerator.Current));

            while (enumerator.MoveNext())
            {
                temp = CombineHashCodes(temp, GetHashCode(enumerator.Current));
            }
        }
        else
        {
            temp = CombineHashCodes(temp, EmptyCollectionPrimeNumber);
        }

        return temp;
    }
}

একটি ভাল অ্যালগরিদম কি করে?

দ্রুততা

একটি হ্যাশ কোড গণনা করে এমন অ্যালগরিদম দ্রুত হওয়া দরকার। একটি সাধারণ অ্যালগরিদম সাধারণত দ্রুততর হতে চলেছে।

নির্ণায়ক

হ্যাশিং অ্যালগরিদমকে ডিটারমিনিস্টিক হওয়া প্রয়োজন অর্থাৎ একই ইনপুট দেওয়া উচিত এটি সর্বদা একই আউটপুট উত্পাদন করে।

সংঘর্ষ হ্রাস করুন

একটি হ্যাশ কোড গণনা করে এমন অ্যালগরিদমকে একটি মিনিটামের সাথে হ্যাশ সংঘর্ষ রাখতে হবে । একটি হ্যাশ সংঘর্ষ এমন পরিস্থিতি যা ঘটে যখন GetHashCodeদুটি পৃথক বস্তুর উপর দুটি কল একইরকম হ্যাশ কোড তৈরি করে। নোট করুন যে সংঘর্ষের অনুমতি রয়েছে (কারও কারও মধ্যে তারা ভুল নয় এমন ভুল ধারণা রয়েছে) তবে সেগুলি সর্বনিম্ন রাখা উচিত।

একটি ভাল হ্যাশ ফাংশনটি প্রত্যাশিত ইনপুটগুলিকে তার আউটপুট সীমার মধ্যে যথাসম্ভব সমান মানচিত্র করা উচিত। এটিতে অভিন্নতা থাকা উচিত।

প্রতিরোধের ডস

.NET কোর প্রতিবার আপনি যখন কোনও অ্যাপ্লিকেশন পুনরায় চালু করবেন তখন আপনি বিভিন্ন হ্যাশ কোড পাবেন। পরিষেবা আক্রমণ (ডিওএস) অস্বীকৃতি রোধ করার জন্য এটি একটি সুরক্ষা বৈশিষ্ট্য। .NET ফ্রেমওয়ার্কের জন্য আপনার নীচের App.config ফাইলটি যুক্ত করে এই বৈশিষ্ট্যটি সক্ষম করা উচিত :

<?xml version ="1.0"?>  
<configuration>  
   <runtime>  
      <UseRandomizedStringHashAlgorithm enabled="1" />  
   </runtime>  
</configuration>

এই বৈশিষ্ট্যটির কারণে, হ্যাশ কোডগুলি অ্যাপ্লিকেশন ডোমেনের বাইরে কখনই ব্যবহার করা উচিত নয় যেখানে তারা তৈরি করা হয়েছিল, সেগুলিকে কখনই কোনও সংগ্রহের মূল ক্ষেত্র হিসাবে ব্যবহার করা উচিত নয় এবং সেগুলি কখনও স্থির করা উচিত নয়।

এখানে এই সম্পর্কে আরও পড়ুন ।

ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত?

অ্যালগরিদম কোনও ক্রিপ্টোগ্রাফিক হ্যাশ ফাংশন হতে পারে না । এর অর্থ নিম্নলিখিত শর্তগুলি পূরণ করতে হবে না:

  • কোনও বার্তা উত্পন্ন করা অসম্ভব যা একটি প্রদত্ত হ্যাশ মান দেয়
  • একই হ্যাশ মান সহ দুটি পৃথক বার্তা পাওয়া অনর্থ্য
  • একটি বার্তায় একটি সামান্য পরিবর্তন হ্যাশ মান এত ব্যাপকভাবে পরিবর্তন করা উচিত যে নতুন হ্যাশ মান পুরানো হ্যাশ মান (তুষারপাত প্রভাব) এর সাথে সম্পর্কহীন প্রদর্শিত হবে।

29

বেশিরভাগ ক্ষেত্রে যেখানে সমান () একাধিক ক্ষেত্রের সাথে তুলনা করে আপনার গেটহ্যাশ () হ্যাশগুলি এক ক্ষেত্রের বা অনেকগুলিতে হ্যাশ করে তা আসলেই কিছু যায় আসে না। আপনাকে কেবল নিশ্চিত করতে হবে যে হ্যাশ গণনা করা সত্যই সস্তা ( কোনও বরাদ্দ নেই , দয়া করে) এবং দ্রুত ( কোনও ভারী গণনা এবং অবশ্যই কোনও ডাটাবেস সংযোগ নেই) এবং একটি ভাল বিতরণ সরবরাহ করে।

ভারী উত্তোলন সমান () পদ্ধতির অংশ হওয়া উচিত; যতটা সম্ভব আইটেমগুলিতে সমান () কল করা সক্ষম করার জন্য হ্যাশটি খুব সস্তা অপারেশন হওয়া উচিত।

এবং একটি চূড়ান্ত টিপ: একাধিক অ্যাপ্লিকেশন রানের উপর স্থিতিশীল হয়ে গেটহ্যাশকোড () এর উপর নির্ভর করবেন না । অনেক। নেট টাইপগুলি পুনরায় চালু হওয়ার পরে তাদের হ্যাশ কোডগুলি একই থাকার জন্য গ্যারান্টি দেয় না, সুতরাং আপনার কেবল মেমরি ডেটা স্ট্রাকচারের জন্য গেটহ্যাশকোড () এর মান ব্যবহার করা উচিত।


10
"বেশিরভাগ ক্ষেত্রে যেখানে সমান () একাধিক ক্ষেত্রের সাথে তুলনা করে তা আপনার গেটহ্যাশ () একটি ক্ষেত্র বা অনেকটিতে হ্যাশ করে আসলেই কিছু আসে যায় না" " এটি বিপজ্জনক পরামর্শ, কারণ কেবলমাত্র অ-হ্যাশযুক্ত ক্ষেত্রগুলিতে পৃথক হওয়া অবজেক্টগুলির জন্য, আপনি হ্যাশের সংঘর্ষ পাবেন। যদি এটি ঘন ঘন ঘটে থাকে তবে হ্যাশ-ভিত্তিক সংগ্রহগুলির (হ্যাশম্যাপ, হ্যাশসেট ইত্যাদি) পারফরম্যান্স হ্রাস পাবে (সবচেয়ে খারাপ ক্ষেত্রে ও (এন) পর্যন্ত)।
স্লেসকে

10
এটি আসলে জাভাতে ঘটেছিল: জেডি কে স্ট্রিং.হ্যাশকোডের প্রাথমিক সংস্করণগুলিতে স্ট্রিংয়ের শুরুটিকে বিবেচনা করা হয়েছিল; যদি আপনি হ্যাশম্যাপসে স্ট্রিংগুলি কী হিসাবে ব্যবহার করেন যা কেবলমাত্র শেষে থাকে (যা সাধারণত ইউআরএলগুলির জন্য সাধারণ)। অ্যালগরিদম তাই পরিবর্তন করা হয়েছে (জেডিকে 1.2 বা 1.3 আমি বিশ্বাস করি)।
স্ল্যাসকে

3
যদি সেই ক্ষেত্রটি 'উত্তরের বিতরণ সরবরাহ করে' (আমার উত্তরের শেষ অংশ), তবে একটি ক্ষেত্রই যথেষ্ট it যদি এটি একটি ভাল বিতরণ সরবরাহ না করে , তবে (এবং ঠিক তখনই) আপনার অন্য গণনা প্রয়োজন। (যেমন, কেবলমাত্র অন্য ক্ষেত্রটি ব্যবহার করুন যা একটি ভাল বিতরণ প্রদান করে , বা একাধিক ক্ষেত্র ব্যবহার করে)
বার্ট হুইজবেন

আমি মনে করি না যে GetHashCodeমেমোরি বরাদ্দগুলি সম্পাদন করার ক্ষেত্রে কোনও সমস্যা আছে , তবে শর্ত থাকে যে এটি কেবল প্রথমবার ব্যবহার করা হয়েছে (পরবর্তী অনুরোধগুলি কেবল একটি ক্যাশেড ফলাফল প্রদান করে)। গুরুত্বপূর্ণ বিষয়টি এই নয় যে সংঘর্ষ এড়ানোর জন্য কারও বড় দৈর্ঘ্যে যাওয়া উচিত নয়, বরং "সিস্টেমিক" সংঘর্ষগুলি এড়ানো উচিত। যদি কোনও ধরণের দুটি intক্ষেত্র থাকে oldXএবং newXযা ঘন ঘন একের সাথে পৃথক হয়, এর একটি হ্যাশ মান oldX^newX1, 2, 4 বা 8 এর রেকর্ড হ্যাশ মানগুলির 90% বরাদ্দ করবে oldX+newX[চেক না করা পাটিগণিত] ব্যবহার করে আরও সংঘর্ষ তৈরি হতে পারে ...
সুপারক্যাট

1
... আরও পরিশীলিত ফাংশন চেয়ে, তবে 500,000 বিভিন্ন হ্যাশ মান রয়েছে এমন 1,000,000 জিনিসগুলির সংকলন খুব ভাল হবে যদি প্রতিটি হ্যাশের মান দুটি সম্পর্কিত জিনিস থাকে এবং খুব খারাপভাবে যদি একটি হ্যাশের মান 500,001 জিনিস থাকে এবং অন্যগুলির মধ্যে একটি থাকে very
সুপারক্যাট

23

সাম্প্রতিক অবধি আমার উত্তর এখানে জোন স্কিটের খুব কাছাকাছি থাকত। যাইহোক, আমি সম্প্রতি একটি প্রকল্প শুরু করেছি যা পাওয়ার-অফ-টু হ্যাশ টেবিল ব্যবহার করেছে, সেটি হ্যাশ টেবিল যেখানে অভ্যন্তরীণ টেবিলের আকার 8, 16, 32, ইত্যাদি রয়েছে prime প্রাইম-সংখ্যা আকারের পক্ষে নেওয়ার পক্ষে যুক্তিসঙ্গত কারণ রয়েছে তবে সেখানে রয়েছে পাওয়ার-অফ-টু মাপের কিছু সুবিধা।

এবং এটি বেশ কিছুটা চুষে ফেলেছে। তাই কিছুটা পরীক্ষা-নিরীক্ষা ও গবেষণার পরে আমি নিম্নলিখিতগুলি দিয়ে আমার হ্যাশগুলিকে পুনরায় হ্যাশ করতে শুরু করেছি:

public static int ReHash(int source)
{
  unchecked
  {
    ulong c = 0xDEADBEEFDEADBEEF + (ulong)source;
    ulong d = 0xE2ADBEEFDEADBEEF ^ c;
    ulong a = d += c = c << 15 | c >> -15;
    ulong b = a += d = d << 52 | d >> -52;
    c ^= b += a = a << 26 | a >> -26;
    d ^= c += b = b << 51 | b >> -51;
    a ^= d += c = c << 28 | c >> -28;
    b ^= a += d = d << 9 | d >> -9;
    c ^= b += a = a << 47 | a >> -47;
    d ^= c += b << 54 | b >> -54;
    a ^= d += c << 32 | c >> 32;
    a += d << 25 | d >> -25;
    return (int)(a >> 1);
  }
}

এবং তারপরে আমার পাওয়ার-অফ-টু হ্যাশ টেবিলটি আর চুষেনি।

যদিও এটি আমাকে বিরক্ত করেছে, কারণ উপরের কাজ করা উচিত নয়। বা আরও স্পষ্টভাবে, এটি নির্দিষ্ট করা উচিত নয় যতক্ষণ না মূলটি GetHashCode()খুব নির্দিষ্ট উপায়ে দরিদ্র না হত।

একটি হ্যাশকোড পুনরায় মেশানো একটি দুর্দান্ত হ্যাশকোড উন্নত করতে পারে না, কারণ একমাত্র সম্ভাব্য প্রভাবটি হ'ল আমরা আরও কয়েকটি সংঘর্ষের পরিচয় দিই।

একটি হ্যাশ কোডটি পুনরায় মিশ্রণ একটি ভয়ানক হ্যাশ কোডটিকে উন্নত করতে পারে না, কারণ কেবলমাত্র সম্ভাব্য প্রভাবটি হ'ল উদাহরণস্বরূপ, 53 মানের একটি বিশাল সংখ্যক সংঘর্ষের একটি বড় সংখ্যার মান 18,3487,291।

একটি হ্যাশ কোডটি পুনরায় মিশ্রিত করা কেবলমাত্র একটি হ্যাশ কোডকে উন্নত করতে পারে যা তার পরিসীমা জুড়ে নিখুঁত সংঘর্ষগুলি এড়িয়ে চলার ক্ষেত্রে কমপক্ষে বেশ ভাল করেছে (2 32 সম্ভাব্য মানগুলি) তবে হ্যাশ টেবিলের প্রকৃত ব্যবহারের জন্য মডুলুড হয়ে গেলে সংঘর্ষগুলি এড়ানোতে খারাপ। পাওয়ার-টু-টেবিলের সরল মডুলো এটিকে আরও প্রকট করে তুলেছে, তবুও এটি আরও সাধারণ প্রাইম-সংখ্যা টেবিলগুলির সাথে নেতিবাচক প্রভাব ফেলছিল, এটি ঠিক তেমন স্পষ্ট ছিল না (পুনর্নির্মাণের অতিরিক্ত কাজ উপকারের চেয়েও বেশি হবে) , তবে সুবিধাটি এখনও থাকবে)।

সম্পাদনা: আমি ওপেন-এড্রেসিংও ব্যবহার করছিলাম, যা সংঘর্ষের সংবেদনশীলতাও বাড়িয়ে তুলত, সম্ভবত এটি পাওয়ার-টু-এর চেয়ে বেশি ছিল।

এবং ভাল, এটি নেট (বা এখানে অধ্যয়ন ) এর string.GetHashCode()বাস্তবায়নগুলি কতটা উন্নত হতে পারে (কম সংঘর্ষের কারণে প্রায় 20-30 গুণ দ্রুত চলমান পরীক্ষার ক্রমে) এবং আমার নিজের হ্যাশ কোডগুলি কতটা বিঘ্নিত করছে তা বিরক্ত করছিল উন্নত করা যেতে পারে (এর চেয়ে অনেক বেশি)।

সকল GetHashCode () বাস্তবায়নের আমি অতীতে কোডেড ছিলাম, এবং প্রকৃতপক্ষে এই সাইটে উত্তর ভিত্তি হিসেবে ব্যবহার করা হয়, অনেক খারাপ চেয়ে আমি throught চাই ছিল । বেশিরভাগ সময় ব্যবহারের জন্য এটি "যথেষ্ট ভাল" ছিল তবে আমি আরও ভাল কিছু চাইছিলাম।

সুতরাং আমি সেই প্রকল্পটি একদিকে রাখলাম (এটি যাইহোক পোষা প্রাণীর প্রকল্প ছিল) এবং কীভাবে। নেট এ খুব ভাল, ভাল বিতরণ করা হ্যাশ কোড উত্পাদন করা যায় তা সন্ধান করতে শুরু করি।

শেষ পর্যন্ত আমি স্পোকিহ্যাশ । নেট এ পোর্ট করার বিষয়ে স্থির হয়েছি । প্রকৃতপক্ষে উপরের কোডটি 32-বিট ইনপুট থেকে 32-বিট আউটপুট উত্পাদন করতে স্পুকিহ্যাশ ব্যবহারের একটি দ্রুতগতির সংস্করণ।

এখন, স্পুকিহ্যাশ কোডের টুকরোগুলি মনে রাখা খুব ভাল নয়। আমার এটির বন্দরটি আরও কম কারণ আমি আরও ভাল গতির জন্য এটির অনেকগুলি হাত-linedোকানো। তবে কোড পুনরায় ব্যবহারের জন্য এটিই।

তারপরে আমি সেই প্রকল্পটিকে একদিকে রাখলাম , কারণ মূল প্রকল্পটি যেমন একটি উন্নত হ্যাশ কোড তৈরি করতে পারে তার প্রশ্ন উত্থাপন করেছিল, সুতরাং সেই প্রকল্পটি কীভাবে আরও ভাল .NET মেমকিপি উত্পাদন করতে পারে সে প্রশ্নটি উত্থাপন করেছিল।

তারপরে আমি ফিরে আসলাম এবং খুব সহজেই প্রায় সমস্ত দেশীয় ধরণের (except ব্যতীত decimal) একটি হ্যাশ কোডে ফিড দেওয়ার জন্য প্রচুর ওভারলোড উত্পাদন করেছি ।

এটি দ্রুত, যার জন্য বব জেনকিন্স বেশিরভাগ কৃতিত্বের দাবিদার, কারণ আমি যে মূল কোডটি দিয়েছিলাম তার মূল কোডটি এখনও দ্রুততর, বিশেষত 64৪-বিট মেশিনে যা g এর জন্য অনুকূলিত হয়েছে ‡

সম্পূর্ণ কোডটি https://bitbucket.org/JonHanna/spookilysharp/src এ দেখা যাবে তবে বিবেচনা করুন যে উপরের কোডটি এর সরলিকৃত সংস্করণ।

তবে এটি যেহেতু ইতিমধ্যে ইতিমধ্যে লেখা হয়েছে, কেউ এটি আরও সহজেই ব্যবহার করতে পারেন:

public override int GetHashCode()
{
  var hash = new SpookyHash();
  hash.Update(field1);
  hash.Update(field2);
  hash.Update(field3);
  return hash.Final().GetHashCode();
}

এটি বীজের মানও গ্রহণ করে, সুতরাং আপনার যদি অবিশ্বস্ত ইনপুট নিয়ে কাজ করতে হয় এবং হ্যাশ ডস আক্রমণ থেকে রক্ষা করতে চান আপনি আপটাইম বা অনুরূপের উপর ভিত্তি করে একটি বীজ সেট করতে পারেন এবং আক্রমণকারীদের দ্বারা ফলাফলটিকে অনির্দেশ্য করতে পারেন:

private static long hashSeed0 = Environment.TickCount;
private static long hashSeed1 = DateTime.Now.Ticks;
public override int GetHashCode()
{
  //produce different hashes ever time this application is restarted
  //but remain consistent in each run, so attackers have a harder time
  //DoSing the hash tables.
  var hash = new SpookyHash(hashSeed0, hashSeed1);
  hash.Update(field1);
  hash.Update(field2);
  hash.Update(field3);
  return hash.Final().GetHashCode();
}

* এটির মধ্যে একটি বড় আশ্চর্য হ'ল একটি ঘূর্ণন পদ্ধতি হ্যান্ড ইনলাইনিং যা (x << n) | (x >> -n)উন্নত জিনিসগুলি ফিরিয়ে দিয়েছে । আমি নিশ্চিত থাকতে পারি যে জিটারটি আমার পক্ষে এটির অন্তর্ভুক্ত ছিল তবে প্রোফাইলিং অন্যথায় প্রদর্শিত হয়েছিল।

decimal.NET দৃষ্টিকোণ থেকে নেটিভ যদিও তা থেকে C # এর নয়। এটা দিয়ে সমস্যা যে তার নিজের GetHashCode()গুরুত্বপূর্ণ হিসাবে একইরূপে স্পষ্টতা যখন নিজস্ব Equals()না। উভয়ই বৈধ পছন্দ, তবে এর মতো মিশ্রিত নয়। আপনার নিজস্ব সংস্করণটি প্রয়োগ করার ক্ষেত্রে আপনার একটি বা অন্যটি বেছে নেওয়া দরকার, তবে আপনি কী চান তা আমি জানতে পারি না।

Comparison তুলনা করার উপায় দ্বারা। যদি স্ট্রিংয়ে ব্যবহার করা হয়, তবে string.GetHashCode()32 বিটের string.GetHashCode()তুলনায় স্পোকি হ্যাশ 32 বিটের তুলনায় যথেষ্ট দ্রুত যা 32 বিটের তুলনায় স্পুকিহ্যাশের তুলনায় যথেষ্ট দ্রুত, যদিও যুক্তিসঙ্গত পছন্দ হিসাবে যথেষ্ট দ্রুত।


একাধিক হ্যাশ মানকে একের সাথে সংযুক্ত করার সময়, আমি longঅন্তর্বর্তী ফলাফলের জন্য মানগুলি ব্যবহার করি এবং তারপরে চূড়ান্ত ফলাফলটিকে নীচে পরিণত করি int। এটি কি একটি ভাল ধারণা বলে মনে হচ্ছে? আমার উদ্বেগ হ'ল যে কোনওটি যেমন হ্যাশ = (হ্যাশ * 31) + নেক্সটফিল্ড ব্যবহার করে, তারপরে মিলের মানগুলির জোড়া হ্যাশের উপরের 27 বিটগুলিকে কেবল প্রভাবিত করবে। গণনাটি একটিতে প্রসারিত করা longএবং মোড়ানো জিনিস stuffোকানো বিপদটি হ্রাস করে।
সুপারক্যাট

@ শুক্র্যাট এটি আপনার চূড়ান্ত মুগিং বিতরণের উপর নির্ভর করে। স্পুকিলিশার্প লাইব্রেরিটি নিশ্চিত করবে যে বিতরণটি ভাল ছিল (আদর্শ কারণ এটি কোনও বস্তু তৈরির প্রয়োজন হবে না) একটি ব্লিটযোগ্য প্রকারের কাছে একটি পয়েন্টারটি পাঠিয়ে, বা এটি সরাসরি হ্যান্ডেল করে এমন একটি গুনে পাস করে, তবে যদি আপনার ইতিমধ্যে ঝাপ্টা না হয় ডেটা বা একটি উপযুক্ত গণনা, তারপরে .Update()উপরের উত্তর অনুসারে একাধিক মান সহ কল করা কৌশলটি করবে।
জন হান্না

@ জোনা হান্না আপনি কি সমস্যাযুক্ত আচরণের সাথে আরও নিখুঁত হতে ইচ্ছুক? আমি এমন একটি লাইব্রেরি বাস্তবায়নের চেষ্টা করছি যা মান অবজেক্টগুলিকে তুচ্ছ ( ভ্যালুউটিলস ) বাস্তবায়িত করে তোলে এবং আমি একটি টেস্টসেটটি পছন্দ করবো যাতে পাওয়ার-টু-হ্যাশ টেবিলের দুর্বল হ্যাশ ভুলত্রুটি প্রদর্শন করা হয়।
ইমন নেরবোন

"সামগ্রিক সময় সেভাবে ধীর ছিল" এর চেয়ে আমার কাছে সত্যই কিছু নেই আমি যেমন একটি সম্পাদনায় যুক্ত করেছি, আমি ওপেন-অ্যাড্রেসিংয়ের বিষয়টি যে পাওয়ার-অফ-টু ফ্যাক্টরের চেয়ে বেশি গুরুত্বপূর্ণ হয়েছিল তা সম্ভবত। আমি একটি নির্দিষ্ট প্রকল্পে কিছু পরীক্ষার কেস করার পরিকল্পনা নিয়েছি যেখানে আমি কয়েকটি ভিন্ন পদ্ধতির তুলনা করব, সুতরাং এটির পরে আমার কাছে আরও ভাল উত্তর থাকতে পারে, যদিও এটি উচ্চ-অগ্রাধিকার নয় (কোনও ব্যক্তিগত চাপের দরকার নেই) , সুতরাং আমি যখন এটি পেয়ে যাব তখনই আমি এটি পেয়ে যাব ...)
জন হান্না

@ জোহান্না: হ্যাঁ আমি জানি ব্যক্তিগত প্রকল্পের সময়সূচিটি কীভাবে চলে - শুভকামনা! যাইহোক, আমি দেখতে পাচ্ছি যে আমি শেষ মন্তব্যটি ভাল করে দেখিনি: আমি বোঝাতে চাইছি সমস্যাযুক্ত ইনপুট চেয়েছি, এবং প্রয়োজনীয় সমস্যাগুলির বিশদটি নয়। আমি এটিকে পরীক্ষার সেট (বা পরীক্ষার সেটের জন্য অনুপ্রেরণা) হিসাবে ব্যবহার করতে চাই। যে কোনও ক্ষেত্রে - আপনার পোষা প্রাণীর প্রকল্পের জন্য শুভকামনা :-)।
ইমন নেরবোন

13

এইটা ভালো:

/// <summary>
/// Helper class for generating hash codes suitable 
/// for use in hashing algorithms and data structures like a hash table. 
/// </summary>
public static class HashCodeHelper
{
    private static int GetHashCodeInternal(int key1, int key2)
    {
        unchecked
        {
           var num = 0x7e53a269;
           num = (-1521134295 * num) + key1;
           num += (num << 10);
           num ^= (num >> 6);

           num = ((-1521134295 * num) + key2);
           num += (num << 10);
           num ^= (num >> 6);

           return num;
        }
    }

    /// <summary>
    /// Returns a hash code for the specified objects
    /// </summary>
    /// <param name="arr">An array of objects used for generating the 
    /// hash code.</param>
    /// <returns>
    /// A hash code, suitable for use in hashing algorithms and data 
    /// structures like a hash table. 
    /// </returns>
    public static int GetHashCode(params object[] arr)
    {
        int hash = 0;
        foreach (var item in arr)
            hash = GetHashCodeInternal(hash, item.GetHashCode());
        return hash;
    }

    /// <summary>
    /// Returns a hash code for the specified objects
    /// </summary>
    /// <param name="obj1">The first object.</param>
    /// <param name="obj2">The second object.</param>
    /// <param name="obj3">The third object.</param>
    /// <param name="obj4">The fourth object.</param>
    /// <returns>
    /// A hash code, suitable for use in hashing algorithms and
    /// data structures like a hash table.
    /// </returns>
    public static int GetHashCode<T1, T2, T3, T4>(T1 obj1, T2 obj2, T3 obj3,
        T4 obj4)
    {
        return GetHashCode(obj1, GetHashCode(obj2, obj3, obj4));
    }

    /// <summary>
    /// Returns a hash code for the specified objects
    /// </summary>
    /// <param name="obj1">The first object.</param>
    /// <param name="obj2">The second object.</param>
    /// <param name="obj3">The third object.</param>
    /// <returns>
    /// A hash code, suitable for use in hashing algorithms and data 
    /// structures like a hash table. 
    /// </returns>
    public static int GetHashCode<T1, T2, T3>(T1 obj1, T2 obj2, T3 obj3)
    {
        return GetHashCode(obj1, GetHashCode(obj2, obj3));
    }

    /// <summary>
    /// Returns a hash code for the specified objects
    /// </summary>
    /// <param name="obj1">The first object.</param>
    /// <param name="obj2">The second object.</param>
    /// <returns>
    /// A hash code, suitable for use in hashing algorithms and data 
    /// structures like a hash table. 
    /// </returns>
    public static int GetHashCode<T1, T2>(T1 obj1, T2 obj2)
    {
        return GetHashCodeInternal(obj1.GetHashCode(), obj2.GetHashCode());
    }
}

এটি এখানে কীভাবে ব্যবহার করবেন তা এখানে:

private struct Key
{
    private Type _type;
    private string _field;

    public Type Type { get { return _type; } }
    public string Field { get { return _field; } }

    public Key(Type type, string field)
    {
        _type = type;
        _field = field;
    }

    public override int GetHashCode()
    {
        return HashCodeHelper.GetHashCode(_field, _type);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is Key))
            return false;
        var tf = (Key)obj;
        return tf._field.Equals(_field) && tf._type.Equals(_type);
    }
}

1
কীগুলি নির্ধারিত হয়? গেটহ্যাশকোড () কোনও প্যারামিটার নেয় না, সুতরাং এটির জন্য দুটি কী দিয়ে এটি কল করা দরকার যা কোনওভাবে নির্ধারণ করা দরকার। দুঃখিত, আরও ব্যাখ্যা ছাড়াই এটি কেবল চালাক দেখাচ্ছে, তবে এটি ভাল নয়।
মাইকেল স্টাম

এবং আপনার জেনেরিক ওভারলোডগুলি কেন দরকার? ধরণের গুরুত্বপূর্ণ নয় (এবং আপনার কোডে ব্যবহৃত হয় না) যেহেতু সমস্ত বস্তুর একটি GetHashCode()পদ্ধতি থাকে তাই আপনি সর্বদা paramsঅ্যারে প্যারামিটার সহ পদ্ধতিটি ব্যবহার করতে পারেন । নাকি আমি এখানে কিছু মিস করছি?
গেহহ

4
যখন আপনি জেনেরিকের পরিবর্তে অবজেক্ট ব্যবহার করবেন আপনি বক্সিং এবং মেমরির বরাদ্দ পেয়ে যাবেন যা আপনি গেটহ্যাশকোডে চান না। তাই জেনেরিকগুলি হল উপায়।
কোডসইনচাউস

1
h += (h << 10); h ^= (h >> 6); h += (h << 3); h ^= (h >> 11); h += (h << 15);ট্রেলিং শিফট / এক্সওর স্টেপস ( একটি কোডমেল রয়েছে: তারা কোনও ইনপুটের উপর নির্ভর করে না এবং আমার কাছে ভীষণ অপ্রয়োজনীয়
দেখায় না

1
@ ম্যাগনাস হ্যাঁ ঠিক আছে, আমি আমার মূল মন্তব্যটি মুছব। কেবলমাত্র একটি সামান্য দ্রষ্টব্য যে এটি অন্য কয়েকটি সমাধানের মতো এখানে দ্রুত নাও হতে পারে, তবে আপনি যা বলেছেন তাতে কিছু আসে যায় না। বিতরণটি এখানে বেশিরভাগ সমাধানের চেয়ে দুর্দান্ত, সুতরাং আমার কাছ থেকে +1! :)
নওফাল

11

Https://github.com/dotnet/coreclr/pull/14863 হিসাবে , হ্যাশ কোডগুলি উত্পন্ন করার জন্য একটি নতুন উপায় রয়েছে যা অতি সাধারণ! শুধু লেখো

public override int GetHashCode()
    => HashCode.Combine(field1, field2, field3);

এটি আপনাকে বাস্তবায়নের বিশদ সম্পর্কে চিন্তা না করে মানসম্পন্ন হ্যাশ কোড উত্পন্ন করবে।


এটি দেখতে একটি মিষ্টি সংযোজনের মতো দেখাচ্ছে ... .NET কোর এর যে সংস্করণটি পাঠানো হবে তা কোনওভাবেই জানবেন?
ড্যান জে

1
@ ডানজে কী খুশির কাকতালীয় ঘটনা, HashCodeআপনার মন্তব্যের কয়েক ঘন্টা আগে কোরফেক্সের জন্য পরিবর্তনগুলি একত্রিত করা হয়েছিল :) টাইপটি নেট কোর ২.১ এ প্রেরণ করা হবে।
জেমস কো

এটি দুর্দান্ত - সম্মত। :)
ড্যান জে

@ ডানজে আরও ভাল খবর - এটি ডটনেট-কোর মাইগেট ফিডে হোস্ট করা কোরিএফএক্সের রাতের বিল্ডগুলিতে এখনই পাওয়া উচিত।
জেমস কো

মিষ্টি - যে আমাকে কর্মক্ষেত্রে সাহায্য না, যেহেতু আমরা বেশ নও যে bleeding-edge, কিন্তু ভালো জানতে। চিয়ার্স!
ড্যান জে

9

এখানে জোন স্কিট দ্বারা পোস্ট করা অ্যালগরিদমের আরেকটি সাবলীল প্রয়োগ রয়েছে, তবে এর মধ্যে কোনও বরাদ্দ বা বক্সিং অপারেশন অন্তর্ভুক্ত নয়:

public static class Hash
{
    public const int Base = 17;

    public static int HashObject(this int hash, object obj)
    {
        unchecked { return hash * 23 + (obj == null ? 0 : obj.GetHashCode()); }
    }

    public static int HashValue<T>(this int hash, T value)
        where T : struct
    {
        unchecked { return hash * 23 + value.GetHashCode(); }
    }
}

ব্যবহার:

public class MyType<T>
{
    public string Name { get; set; }

    public string Description { get; set; }

    public int Value { get; set; }

    public IEnumerable<T> Children { get; set; }

    public override int GetHashCode()
    {
        return Hash.Base
            .HashObject(this.Name)
            .HashObject(this.Description)
            .HashValue(this.Value)
            .HashObject(this.Children);
    }
}

সংকলকটি নিশ্চিত করবে HashValueযে জেনেরিক ধরণের সীমাবদ্ধতার কারণে কোনও শ্রেণীর সাথে ডাকা হয়নি। HashObjectজেনেরিক যুক্তি যুক্ত করে একটি বক্সিং অপারেশন যুক্ত করার জন্য কোনও সংকলক সমর্থন নেই ।


8

এখানে আমার সরল পদ্ধতির উপায়। আমি এটির জন্য ক্লাসিক বিল্ডার প্যাটার্নটি ব্যবহার করছি। এটি টাইপসেফ (কোনও বক্সিং / আনবক্সিং নয়) এবং নেট নেট ২.০ (কোনও এক্সটেনশন পদ্ধতি নেই) এর সাথে তুলনা করে।

এটি এভাবে ব্যবহার করা হয়:

public override int GetHashCode()
{
    HashBuilder b = new HashBuilder();
    b.AddItems(this.member1, this.member2, this.member3);
    return b.Result;
} 

এবং এখানে একিউটাল বিল্ডার শ্রেণি রয়েছে:

internal class HashBuilder
{
    private const int Prime1 = 17;
    private const int Prime2 = 23;
    private int result = Prime1;

    public HashBuilder()
    {
    }

    public HashBuilder(int startHash)
    {
        this.result = startHash;
    }

    public int Result
    {
        get
        {
            return this.result;
        }
    }

    public void AddItem<T>(T item)
    {
        unchecked
        {
            this.result = this.result * Prime2 + item.GetHashCode();
        }
    }

    public void AddItems<T1, T2>(T1 item1, T2 item2)
    {
        this.AddItem(item1);
        this.AddItem(item2);
    }

    public void AddItems<T1, T2, T3>(T1 item1, T2 item2, T3 item3)
    {
        this.AddItem(item1);
        this.AddItem(item2);
        this.AddItem(item3);
    }

    public void AddItems<T1, T2, T3, T4>(T1 item1, T2 item2, T3 item3, 
        T4 item4)
    {
        this.AddItem(item1);
        this.AddItem(item2);
        this.AddItem(item3);
        this.AddItem(item4);
    }

    public void AddItems<T1, T2, T3, T4, T5>(T1 item1, T2 item2, T3 item3, 
        T4 item4, T5 item5)
    {
        this.AddItem(item1);
        this.AddItem(item2);
        this.AddItem(item3);
        this.AddItem(item4);
        this.AddItem(item5);
    }        

    public void AddItems<T>(params T[] items)
    {
        foreach (T item in items)
        {
            this.AddItem(item);
        }
    }
}

আপনি গঙ্গাসকোড ফাংশনের ভিতরে ম্যাঙ্গাসের উত্তর হিসাবে অবজেক্ট তৈরি এড়াতে পারবেন। শুধু অভিহিত স্ট্যাটিক হ্যাশ ফাংশনগুলি কল করুন (যিনি স্টার্টার হ্যাশ সম্পর্কে যত্নশীল)। এছাড়াও, আপনি AddItems<T>(params T[] items)সহায়ক ক্লাসে আরও প্রায়শই পদ্ধতিটি ব্যবহার করতে পারেন ( AddItem(T)প্রতিটি সময় কল করার চেয়ে )।
নওফাল

এবং this.result * Prime2 * item.GetHashCode()প্রায়শই ব্যবহৃত হওয়ার পরে আপনি কী সুবিধা পান this.result * Prime2 + item.GetHashCode()?
নওফাল

আমি AddItems<T>(params T[] items)বেশি বেশি ব্যবহার করতে পারি না কারণ typeof(T1) != typeof(T2)ইত্যাদি
বিটবঙ্ক

ওহ হ্যাঁ আমি এটা মিস করেছি
নওফাল

5

রিশার্পার ব্যবহারকারীরা গেটহ্যাশকোড, সমতুল্য এবং অন্যান্য সহ উত্পন্ন করতে পারে ReSharper -> Edit -> Generate Code -> Equality Members

// ReSharper's GetHashCode looks like this
public override int GetHashCode() {
    unchecked {
        int hashCode = Id;
        hashCode = (hashCode * 397) ^ IntMember;
        hashCode = (hashCode * 397) ^ OtherIntMember;
        hashCode = (hashCode * 397) ^ (RefMember != null ? RefMember.GetHashCode() : 0);
        // ...
        return hashCode;
    }
}

4

যদি আমাদের 8 টিরও বেশি সম্পত্তি না থাকে (আশা করি), এখানে অন্য বিকল্প রয়েছে।

ValueTupleএটি একটি কাঠামো এবং একটি দৃ GetHashCodeimplementation ় বাস্তবায়ন রয়েছে বলে মনে হয় ।

এর অর্থ আমরা কেবল এটি করতে পারি:

// Yay, no allocations and no custom implementations!
public override int GetHashCode() => (this.PropA, this.PropB).GetHashCode();

আসুন জন্য .NET কোর বর্তমান বাস্তবায়ন কটাক্ষপাত করা ValueTuple'র GetHashCode

এটি থেকে ValueTuple:

    internal static int CombineHashCodes(int h1, int h2)
    {
        return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2);
    }

    internal static int CombineHashCodes(int h1, int h2, int h3)
    {
        return HashHelpers.Combine(CombineHashCodes(h1, h2), h3);
    }

এবং এটি থেকে HashHelper:

    public static readonly int RandomSeed = Guid.NewGuid().GetHashCode();

    public static int Combine(int h1, int h2)
    {
        unchecked
        {
            // RyuJIT optimizes this to use the ROL instruction
            // Related GitHub pull request: dotnet/coreclr#1830
            uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27);
            return ((int)rol5 + h1) ^ h2;
        }
    }

ইংরেজীতে:

  • বাম ঘোরান (বিজ্ঞপ্তি স্থানান্তর) এইচ 1 দ্বারা 5 পজিশন।
  • ফলাফল এবং h1 একসাথে যুক্ত করুন।
  • এইচ 2 সহ ফলাফলটি এক্সওআর করুন।
  • {স্ট্যাটিক এলোমেলো বীজ, h1 on উপরের ক্রিয়াকলাপটি সম্পাদন করে শুরু করুন}
  • প্রতিটি পরবর্তী আইটেমের জন্য, পূর্ববর্তী ফলাফল এবং পরবর্তী আইটেমটিতে (যেমন h2) অপারেশন করুন।

এই রোল -5 হ্যাশ কোড অ্যালগরিদমের বৈশিষ্ট্যগুলি সম্পর্কে আরও জানলে ভালো লাগবে।

আফসোস, ValueTupleআমাদের নিজের জন্য পিছিয়ে দেওয়া GetHashCodeআমরা যতটা ইচ্ছা আশা করি এবং তত দ্রুত হতে পারে না। সম্পর্কিত আলোচনার এই মন্তব্যটিHashHelpers.Combine বোঝায় যে সরাসরি কল করা আরও পারফরম্যান্ট। ফ্লিপ দিকে, এটি একটি অভ্যন্তরীণ, সুতরাং আমরা কোডটি অনুলিপি করতে হবে, এখানে আমরা যা অর্জন করেছি তার বেশিরভাগ বলিদান করতে হবে। এছাড়াও, আমরা Combineএলোমেলো বীজের সাথে প্রথমে মনে রাখার জন্য দায়বদ্ধ থাকব । আমি জানি না যে আমরা যদি এই পদক্ষেপটি এড়িয়ে যাই তবে কী পরিণতি হবে।


ধরে h1 >> 27নেওয়া 0 এটিকে উপেক্ষা করার জন্য এটি h1 << 5সমান h1 * 32তাই এটি সমান h1 * 33 ^ h2এই পৃষ্ঠা অনুসারে , এটি "পরিবর্তিত বার্নস্টেইন" নামে পরিচিত।
ক্যাক্টুয়ারয়েড

3

আমার বেশিরভাগ কাজ ডেটাবেস সংযোগের সাথে সম্পন্ন হয়েছে যার অর্থ আমার ক্লাসগুলির সকলের ডাটাবেস থেকে একটি অনন্য শনাক্তকারী রয়েছে। আমি সবসময় হ্যাশকোড তৈরি করতে ডাটাবেস থেকে আইডি ব্যবহার করি।

// Unique ID from database
private int _id;

...    
{
  return _id.GetHashCode();
}

এর অর্থ হ'ল যদি আপনার কাছে ব্যক্তি এবং অ্যাকাউন্ট অবজেক্ট থাকে এবং তাদের উভয়ই এবং আইডি = 1 থাকে তবে তাদের একই হ্যাশ কোড থাকবে। এবং এটা ঠিক নেই।
পেরো

15
আসলে উপরের মন্তব্যটি ভুল। সর্বদা হ্যাশ-কোড সংঘর্ষের সম্ভাবনা থাকবে (একটি হ্যাশ কোড কেবল বালতিটি সনাক্ত করে, পৃথক বস্তু নয়)। সুতরাং এই জাতীয় প্রয়োগ - মিশ্র বস্তুযুক্ত একটি হ্যাশকোডের জন্য - প্রচুর সংঘর্ষের দিকে পরিচালিত করবে, যা অনাকাঙ্ক্ষিত but এছাড়াও এটি সমানভাবে বিতরণ করে না, তবুও সিস্টেম.বজেক্টের ভিত্তি বাস্তবায়ন করে না, তাই আমি এটি নিয়ে খুব বেশি চিন্তা করব না ...
পাইরেস 7

2
হ্যাশ কোডটি কেবল আইডি হতে পারে, যেহেতু আইডি একটি পূর্ণসংখ্যা। কোনও পূর্ণসংখ্যায় গেটহ্যাশকোড কল করার দরকার নেই (এটি একটি পরিচয় ফাংশন)
ডারেল লি

2
@ ড্যারেললি তবে টোমো তার _আইড গাইড হতে পারে। _id.GetHashCodeউদ্দেশ্যটি পরিষ্কার হওয়ার সাথে সাথে এটি করা একটি ভাল কোডিং অনুশীলন ।
নওফাল

2
@ 1224 ব্যবহারের ধরণগুলির উপর নির্ভর করে আপনি যে কারণে দিয়েছেন তা ভয়াবহ হতে পারে তবে এটি দুর্দান্তও হতে পারে; যদি আপনি কোনও গর্ত ছাড়াই এই জাতীয় সংখ্যার ক্রম হন তবে আপনার একটি সঠিক হ্যাশ, কোনও অ্যালগরিদম উত্পাদন করতে পারে তার চেয়ে ভাল। যদি আপনি এটি জানেন তবে আপনি এটির উপর নির্ভর করতে পারেন এবং সমতা পরীক্ষাটি এড়িয়ে যেতে পারেন।
জন হান্না

3

আপনি চাইলে প্রাইমগুলি বাড়ানো আরও সহজ হ'ল নাইটকোডারের সমাধানের সাথে অনেকগুলি একই রকম।

পিএস: এটি সেই সময়গুলির মধ্যে একটি যেখানে আপনি নিজের মুখে খানিকটা ঝাঁকুনি জেনেন যে এটি 9 টি ডিফল্ট দ্বারা একটি পদ্ধতিতে রিফ্যাক্টর করা যেতে পারে তবে এটি ধীর হবে, সুতরাং আপনি কেবল চোখ বন্ধ করে এটি ভুলে যাওয়ার চেষ্টা করবেন।

/// <summary>
/// Try not to look at the source code. It works. Just rely on it.
/// </summary>
public static class HashHelper
{
    private const int PrimeOne = 17;
    private const int PrimeTwo = 23;

    public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();
            hash = hash * PrimeTwo + arg3.GetHashCode();
            hash = hash * PrimeTwo + arg4.GetHashCode();
            hash = hash * PrimeTwo + arg5.GetHashCode();
            hash = hash * PrimeTwo + arg6.GetHashCode();
            hash = hash * PrimeTwo + arg7.GetHashCode();
            hash = hash * PrimeTwo + arg8.GetHashCode();
            hash = hash * PrimeTwo + arg9.GetHashCode();
            hash = hash * PrimeTwo + arg10.GetHashCode();

            return hash;
        }
    }

    public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();
            hash = hash * PrimeTwo + arg3.GetHashCode();
            hash = hash * PrimeTwo + arg4.GetHashCode();
            hash = hash * PrimeTwo + arg5.GetHashCode();
            hash = hash * PrimeTwo + arg6.GetHashCode();
            hash = hash * PrimeTwo + arg7.GetHashCode();
            hash = hash * PrimeTwo + arg8.GetHashCode();
            hash = hash * PrimeTwo + arg9.GetHashCode();

            return hash;
        }
    }

    public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();
            hash = hash * PrimeTwo + arg3.GetHashCode();
            hash = hash * PrimeTwo + arg4.GetHashCode();
            hash = hash * PrimeTwo + arg5.GetHashCode();
            hash = hash * PrimeTwo + arg6.GetHashCode();
            hash = hash * PrimeTwo + arg7.GetHashCode();
            hash = hash * PrimeTwo + arg8.GetHashCode();

            return hash;
        }
    }

    public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();
            hash = hash * PrimeTwo + arg3.GetHashCode();
            hash = hash * PrimeTwo + arg4.GetHashCode();
            hash = hash * PrimeTwo + arg5.GetHashCode();
            hash = hash * PrimeTwo + arg6.GetHashCode();
            hash = hash * PrimeTwo + arg7.GetHashCode();

            return hash;
        }
    }

    public static int GetHashCode<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();
            hash = hash * PrimeTwo + arg3.GetHashCode();
            hash = hash * PrimeTwo + arg4.GetHashCode();
            hash = hash * PrimeTwo + arg5.GetHashCode();
            hash = hash * PrimeTwo + arg6.GetHashCode();

            return hash;
        }
    }

    public static int GetHashCode<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();
            hash = hash * PrimeTwo + arg3.GetHashCode();
            hash = hash * PrimeTwo + arg4.GetHashCode();
            hash = hash * PrimeTwo + arg5.GetHashCode();

            return hash;
        }
    }

    public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();
            hash = hash * PrimeTwo + arg3.GetHashCode();
            hash = hash * PrimeTwo + arg4.GetHashCode();

            return hash;
        }
    }

    public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();
            hash = hash * PrimeTwo + arg3.GetHashCode();

            return hash;
        }
    }

    public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
    {
        unchecked
        {
            int hash = PrimeOne;
            hash = hash * PrimeTwo + arg1.GetHashCode();
            hash = hash * PrimeTwo + arg2.GetHashCode();

            return hash;
        }
    }
}

2
নাল হ্যান্ডেল করে না
জেজেএস

1

আমি উপরের উত্তর হিসাবে নির্বাচিত বাস্তবায়নটি ব্যবহার করে ভাসমান এবং দশমিক সমেত একটি ইস্যুতে দৌড়েছি।

এই পরীক্ষাটি ব্যর্থ হয় (ভাসমান; হ্যাশ একই যদিও আমি 2 টি মানকে নেতিবাচক হতে চলেছি):

        var obj1 = new { A = 100m, B = 100m, C = 100m, D = 100m};
        var obj2 = new { A = 100m, B = 100m, C = -100m, D = -100m};
        var hash1 = ComputeHash(obj1.A, obj1.B, obj1.C, obj1.D);
        var hash2 = ComputeHash(obj2.A, obj2.B, obj2.C, obj2.D);
        Assert.IsFalse(hash1 == hash2, string.Format("Hashcode values should be different   hash1:{0}  hash2:{1}",hash1,hash2));

তবে এই পরীক্ষাটি পাস করেছে (ints সহ):

        var obj1 = new { A = 100m, B = 100m, C = 100, D = 100};
        var obj2 = new { A = 100m, B = 100m, C = -100, D = -100};
        var hash1 = ComputeHash(obj1.A, obj1.B, obj1.C, obj1.D);
        var hash2 = ComputeHash(obj2.A, obj2.B, obj2.C, obj2.D);
        Assert.IsFalse(hash1 == hash2, string.Format("Hashcode values should be different   hash1:{0}  hash2:{1}",hash1,hash2));

আদিম ধরণের জন্য গেটহ্যাশকোড ব্যবহার না করার জন্য আমি আমার বাস্তবায়ন পরিবর্তন করেছি এবং এটি আরও ভাল কাজ করছে বলে মনে হচ্ছে

    private static int InternalComputeHash(params object[] obj)
    {
        unchecked
        {
            var result = (int)SEED_VALUE_PRIME;
            for (uint i = 0; i < obj.Length; i++)
            {
                var currval = result;
                var nextval = DetermineNextValue(obj[i]);
                result = (result * MULTIPLIER_VALUE_PRIME) + nextval;

            }
            return result;
        }
    }



    private static int DetermineNextValue(object value)
    {
        unchecked
        {

                int hashCode;
                if (value is short
                    || value is int
                    || value is byte
                    || value is sbyte
                    || value is uint
                    || value is ushort
                    || value is ulong
                    || value is long
                    || value is float
                    || value is double
                    || value is decimal)
                {
                    return Convert.ToInt32(value);
                }
                else
                {
                    return value != null ? value.GetHashCode() : 0;
                }
        }
    }

1
যদি আপনি অন্যথায় অভিপ্রেত uncheckedপ্রভাবিত করে না Convert.ToInt32: uint, long, float, doubleএবং decimalএখানে সব ওভারফ্লো পারেন।
মার্ক হার্ট

1

মাইক্রোসফ্ট হ্যাশিংয়ের বিভিন্ন উপায়ে নেতৃত্ব ...

//for classes that contain a single int value
return this.value;

//for classes that contain multiple int value
return x ^ y;

//for classes that contain single number bigger than int    
return ((int)value ^ (int)(value >> 32)); 

//for classes that contain class instance fields which inherit from object
return obj1.GetHashCode();

//for classes that contain multiple class instance fields which inherit from object
return obj1.GetHashCode() ^ obj2.GetHashCode() ^ obj3.GetHashCode(); 

আমি অনুমান করতে পারি যে একাধিক বড় শিল্পের জন্য আপনি এটি ব্যবহার করতে পারেন:

int a=((int)value1 ^ (int)(value1 >> 32));
int b=((int)value2 ^ (int)(value2 >> 32));
int c=((int)value3 ^ (int)(value3 >> 32));
return a ^ b ^ c;

এবং বহুধরণের জন্য একই: সমস্তটি প্রথমে intব্যবহারের ক্ষেত্রে রূপান্তরিত হয় GetHashCode()তারপরে মানগুলি xor'ed হবে এবং ফলাফলটি হ্যাশ করবে।

যারা আইডি হিসাবে হ্যাশ ব্যবহার করেন (যার অর্থ একটি অনন্য মান), হ্যাশটি স্বাভাবিকভাবেই বেশ কয়েকটি অঙ্কের মধ্যে সীমাবদ্ধ থাকে, আমি মনে করি এটি হ্যাশিং অ্যালগরিদমের জন্য কমপক্ষে এমডি 5 এর জন্য 5 বাইট ছিল।

আপনি একাধিক মান হ্যাশ মানে পরিণত করতে পারেন এবং সেগুলির কিছু একই হতে পারে, সুতরাং এটি সনাক্তকারী হিসাবে ব্যবহার করবেন না। (সম্ভবত কোনও দিন আমি আপনার উপাদান ব্যবহার করতে যাচ্ছি)


7
হ্যাশকোড তৈরি করতে জোরিং ইন্টিজারগুলি একটি সুপরিচিত অ্যান্টিপ্যাটার্ন যা বাস্তব-বিশ্বের মূল্যবোধগুলির সাথে বিশেষত সংখ্যক সংঘর্ষের ফলস্বরূপ থাকে।
জন হান্না

এখানে প্রত্যেকে প্রত্যেকেই পূর্ণসংখ্যা ব্যবহার করে এবং হ্যাশের জন্য কোনও ধরণের গ্যারান্টি কখনও পাওয়া যায়নি, এটি সংঘর্ষ হওয়ার মতো সংখ্যক যতটা ততটা পরিবর্তিত হওয়ার চেষ্টা করেছিল।
মৃতম্যান

হ্যাঁ, তবে আপনার দ্বিতীয় এবং পঞ্চম সংঘর্ষ এড়ানোর চেষ্টা করবেন না।
জন হান্না

1
হ্যাঁ, এন্টিপ্যাটার্ন বেশ সাধারণ।
জন হান্না

2
পৌঁছানোর জন্য একটি ভারসাম্য আছে। স্পুকিহ্যাশের মতো সত্যিই একটি ভাল হ্যাশ কোড ব্যবহার করুন এবং আপনি অনেকগুলি, আরও ভাল সংঘর্ষ এড়ানো এড়াতে পারবেন তবে এর যে কোনওটির তুলনায় এর অনেক বেশি গণনার সময় হবে (তবে যখন এটি খুব বড় পরিমাণে ডেটা হ্যাশিংয়ের কথা আসে তখন স্পোকিহ্যাশ অত্যন্ত দ্রুত)। জোরিংয়ের আগে মানগুলির একটিতে একটি সরল শিফট হ'ল সংঘর্ষে ভাল হ্রাসের জন্য কেবলমাত্র প্রান্তিক অতিরিক্ত ব্যয়। প্রাইম-সংখ্যা গুণ আবার এবং সময় উভয় বৃদ্ধি। যা শিফট বা বহুগুলির মধ্যে ভাল তাই বিতর্কযোগ্য। সরল জোর যদিও খুব প্রায়ই আসল তথ্যগুলিতে প্রচুর সংঘর্ষ হয় এবং এটি এড়ানো যায়
জন হান্না

1

এটি স্ট্যাটিক সহায়ক শ্রেণি যা জোশ ব্লচের প্রয়োগ বাস্তবায়ন করে; এবং বক্সিং "প্রতিরোধ" করার জন্য স্পষ্টত ওভারলোড সরবরাহ করে এবং বিশেষত দীর্ঘ আদিমদের জন্য হ্যাশটি বাস্তবায়িত করতে।

আপনি একটি স্ট্রিং তুলনা পাস করতে পারেন যা আপনার সমান বাস্তবায়নের সাথে মেলে।

হ্যাশ আউটপুট সর্বদা একটি অন্তর্নিহিত, আপনি কেবল হ্যাশ কল চেইন করতে পারেন।

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;


namespace Sc.Util.System
{
    /// <summary>
    /// Static methods that allow easy implementation of hashCode. Example usage:
    /// <code>
    /// public override int GetHashCode()
    ///     => HashCodeHelper.Seed
    ///         .Hash(primitiveField)
    ///         .Hsh(objectField)
    ///         .Hash(iEnumerableField);
    /// </code>
    /// </summary>
    public static class HashCodeHelper
    {
        /// <summary>
        /// An initial value for a hashCode, to which is added contributions from fields.
        /// Using a non-zero value decreases collisions of hashCode values.
        /// </summary>
        public const int Seed = 23;

        private const int oddPrimeNumber = 37;


        /// <summary>
        /// Rotates the seed against a prime number.
        /// </summary>
        /// <param name="aSeed">The hash's first term.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static int rotateFirstTerm(int aSeed)
        {
            unchecked {
                return HashCodeHelper.oddPrimeNumber * aSeed;
            }
        }


        /// <summary>
        /// Contributes a boolean to the developing HashCode seed.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aBoolean">The value to contribute.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(this int aSeed, bool aBoolean)
        {
            unchecked {
                return HashCodeHelper.rotateFirstTerm(aSeed)
                        + (aBoolean
                                ? 1
                                : 0);
            }
        }

        /// <summary>
        /// Contributes a char to the developing HashCode seed.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aChar">The value to contribute.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(this int aSeed, char aChar)
        {
            unchecked {
                return HashCodeHelper.rotateFirstTerm(aSeed)
                        + aChar;
            }
        }

        /// <summary>
        /// Contributes an int to the developing HashCode seed.
        /// Note that byte and short are handled by this method, through implicit conversion.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aInt">The value to contribute.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(this int aSeed, int aInt)
        {
            unchecked {
                return HashCodeHelper.rotateFirstTerm(aSeed)
                        + aInt;
            }
        }

        /// <summary>
        /// Contributes a long to the developing HashCode seed.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aLong">The value to contribute.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(this int aSeed, long aLong)
        {
            unchecked {
                return HashCodeHelper.rotateFirstTerm(aSeed)
                        + (int)(aLong ^ (aLong >> 32));
            }
        }

        /// <summary>
        /// Contributes a float to the developing HashCode seed.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aFloat">The value to contribute.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(this int aSeed, float aFloat)
        {
            unchecked {
                return HashCodeHelper.rotateFirstTerm(aSeed)
                        + Convert.ToInt32(aFloat);
            }
        }

        /// <summary>
        /// Contributes a double to the developing HashCode seed.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aDouble">The value to contribute.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(this int aSeed, double aDouble)
            => aSeed.Hash(Convert.ToInt64(aDouble));

        /// <summary>
        /// Contributes a string to the developing HashCode seed.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aString">The value to contribute.</param>
        /// <param name="stringComparison">Optional comparison that creates the hash.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(
                this int aSeed,
                string aString,
                StringComparison stringComparison = StringComparison.Ordinal)
        {
            if (aString == null)
                return aSeed.Hash(0);
            switch (stringComparison) {
                case StringComparison.CurrentCulture :
                    return StringComparer.CurrentCulture.GetHashCode(aString);
                case StringComparison.CurrentCultureIgnoreCase :
                    return StringComparer.CurrentCultureIgnoreCase.GetHashCode(aString);
                case StringComparison.InvariantCulture :
                    return StringComparer.InvariantCulture.GetHashCode(aString);
                case StringComparison.InvariantCultureIgnoreCase :
                    return StringComparer.InvariantCultureIgnoreCase.GetHashCode(aString);
                case StringComparison.OrdinalIgnoreCase :
                    return StringComparer.OrdinalIgnoreCase.GetHashCode(aString);
                default :
                    return StringComparer.Ordinal.GetHashCode(aString);
            }
        }

        /// <summary>
        /// Contributes a possibly-null array to the developing HashCode seed.
        /// Each element may be a primitive, a reference, or a possibly-null array.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aArray">CAN be null.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(this int aSeed, IEnumerable aArray)
        {
            if (aArray == null)
                return aSeed.Hash(0);
            int countPlusOne = 1; // So it differs from null
            foreach (object item in aArray) {
                ++countPlusOne;
                if (item is IEnumerable arrayItem) {
                    if (!object.ReferenceEquals(aArray, arrayItem))
                        aSeed = aSeed.Hash(arrayItem); // recursive call!
                } else
                    aSeed = aSeed.Hash(item);
            }
            return aSeed.Hash(countPlusOne);
        }

        /// <summary>
        /// Contributes a possibly-null array to the developing HashCode seed.
        /// You must provide the hash function for each element.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aArray">CAN be null.</param>
        /// <param name="hashElement">Required: yields the hash for each element
        /// in <paramref name="aArray"/>.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash<T>(this int aSeed, IEnumerable<T> aArray, Func<T, int> hashElement)
        {
            if (aArray == null)
                return aSeed.Hash(0);
            int countPlusOne = 1; // So it differs from null
            foreach (T item in aArray) {
                ++countPlusOne;
                aSeed = aSeed.Hash(hashElement(item));
            }
            return aSeed.Hash(countPlusOne);
        }

        /// <summary>
        /// Contributes a possibly-null object to the developing HashCode seed.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="aObject">CAN be null.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Hash(this int aSeed, object aObject)
        {
            switch (aObject) {
                case null :
                    return aSeed.Hash(0);
                case bool b :
                    return aSeed.Hash(b);
                case char c :
                    return aSeed.Hash(c);
                case int i :
                    return aSeed.Hash(i);
                case long l :
                    return aSeed.Hash(l);
                case float f :
                    return aSeed.Hash(f);
                case double d :
                    return aSeed.Hash(d);
                case string s :
                    return aSeed.Hash(s);
                case IEnumerable iEnumerable :
                    return aSeed.Hash(iEnumerable);
            }
            return aSeed.Hash(aObject.GetHashCode());
        }


        /// <summary>
        /// This utility method uses reflection to iterate all specified properties that are readable
        /// on the given object, excluding any property names given in the params arguments, and
        /// generates a hashcode.
        /// </summary>
        /// <param name="aSeed">The developing hash code, or the seed: if you have no seed, use
        /// the <see cref="Seed"/>.</param>
        /// <param name="aObject">CAN be null.</param>
        /// <param name="propertySelector"><see cref="BindingFlags"/> to select the properties to hash.</param>
        /// <param name="ignorePropertyNames">Optional.</param>
        /// <returns>A hash from the properties contributed to <c>aSeed</c>.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int HashAllProperties(
                this int aSeed,
                object aObject,
                BindingFlags propertySelector
                        = BindingFlags.Instance
                        | BindingFlags.Public
                        | BindingFlags.GetProperty,
                params string[] ignorePropertyNames)
        {
            if (aObject == null)
                return aSeed.Hash(0);
            if ((ignorePropertyNames != null)
                    && (ignorePropertyNames.Length != 0)) {
                foreach (PropertyInfo propertyInfo in aObject.GetType()
                        .GetProperties(propertySelector)) {
                    if (!propertyInfo.CanRead
                            || (Array.IndexOf(ignorePropertyNames, propertyInfo.Name) >= 0))
                        continue;
                    aSeed = aSeed.Hash(propertyInfo.GetValue(aObject));
                }
            } else {
                foreach (PropertyInfo propertyInfo in aObject.GetType()
                        .GetProperties(propertySelector)) {
                    if (propertyInfo.CanRead)
                        aSeed = aSeed.Hash(propertyInfo.GetValue(aObject));
                }
            }
            return aSeed;
        }


        /// <summary>
        /// NOTICE: this method is provided to contribute a <see cref="KeyValuePair{TKey,TValue}"/> to
        /// the developing HashCode seed; by hashing the key and the value independently. HOWEVER,
        /// this method has a different name since it will not be automatically invoked by
        /// <see cref="Hash(int,object)"/>, <see cref="Hash(int,IEnumerable)"/>,
        /// or <see cref="HashAllProperties"/> --- you MUST NOT mix this method with those unless
        /// you are sure that no KeyValuePair instances will be passed to those methods; or otherwise
        /// the generated hash code will not be consistent. This method itself ALSO will not invoke
        /// this method on the Key or Value here if that itself is a KeyValuePair.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="keyValuePair">The value to contribute.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int HashKeyAndValue<TKey, TValue>(this int aSeed, KeyValuePair<TKey, TValue> keyValuePair)
            => aSeed.Hash(keyValuePair.Key)
                    .Hash(keyValuePair.Value);

        /// <summary>
        /// NOTICE: this method is provided to contribute a collection of <see cref="KeyValuePair{TKey,TValue}"/>
        /// to the developing HashCode seed; by hashing the key and the value independently. HOWEVER,
        /// this method has a different name since it will not be automatically invoked by
        /// <see cref="Hash(int,object)"/>, <see cref="Hash(int,IEnumerable)"/>,
        /// or <see cref="HashAllProperties"/> --- you MUST NOT mix this method with those unless
        /// you are sure that no KeyValuePair instances will be passed to those methods; or otherwise
        /// the generated hash code will not be consistent. This method itself ALSO will not invoke
        /// this method on a Key or Value here if that itself is a KeyValuePair or an Enumerable of
        /// KeyValuePair.
        /// </summary>
        /// <param name="aSeed">The developing HashCode value or seed.</param>
        /// <param name="keyValuePairs">The values to contribute.</param>
        /// <returns>The new hash code.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int HashKeysAndValues<TKey, TValue>(
                this int aSeed,
                IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs)
        {
            if (keyValuePairs == null)
                return aSeed.Hash(null);
            foreach (KeyValuePair<TKey, TValue> keyValuePair in keyValuePairs) {
                aSeed = aSeed.HashKeyAndValue(keyValuePair);
            }
            return aSeed;
        }
    }
}

হাঁ: আমি একটি বাগ খুঁজে পেয়েছি! HashKeysAndValuesপদ্ধতি করেছে সংশোধন করা: এটা এমন কিছুকে ডাকে, HashKeyAndValue
স্টিভেন কোকো

0

যদি আপনি polyfill করতে চান HashCodeথেকেnetstandard2.1

public static class HashCode
{
    public static int Combine(params object[] instances)
    {
        int hash = 17;

        foreach (var i in instances)
        {
            hash = unchecked((hash * 31) + (i?.GetHashCode() ?? 0));
        }

        return hash;
    }
}

দ্রষ্টব্য: যদি structএটির সাথে ব্যবহার করা হয় তবে এটি বক্সিংয়ের কারণে মেমরির বরাদ্দ করবে

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.