অবজেক্ট.গেটহ্যাশকোড () এর জন্য ডিফল্ট বাস্তবায়ন


162

কিভাবে GetHashCode()কাজের জন্য ডিফল্ট বাস্তবায়ন ? এবং এটি কী কাঠামো, শ্রেণি, অ্যারে ইত্যাদি দক্ষতার সাথে এবং যথেষ্টভাবে পরিচালনা করে?

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


নিবন্ধটিতে আমি যে মন্তব্যটি রেখেছি তাতে একবার নজর দিন: stackoverflow.com/questions/763731/gethashcode- এক্সটেনশন- আদর্শ
পল ওয়েস্টকোট


34
একপাশে: আপনি করতে পারেন প্রাপ্ত ডিফল্ট হ্যাশকোড (এমনকি যখন GetHashCode()ব্যবহার দ্বারা অধিলিখিত হয়েছে)System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj)
মার্ক Gravell

@ মার্কগ্রাভেল আপনাকে এই অবদানের জন্য ধন্যবাদ, আমি ঠিক এই উত্তরটির সন্ধান করছিলাম।
অ্যান্ড্রু সাভিনিখ

@ মার্কগ্রাভেল তবে আমি অন্যান্য পদ্ধতিতে এটি কীভাবে করব?
টোম জ্যাটো - মনিকা

উত্তর:


86
namespace System {
    public class Object {
        [MethodImpl(MethodImplOptions.InternalCall)]
        internal static extern int InternalGetHashCode(object obj);

        public virtual int GetHashCode() {
            return InternalGetHashCode(this);
        }
    }
}

ইন্টারনালগেটহ্যাশকোড সিএলআরতে একটি অবজেক্টেটিভ :: গেটহ্যাশকোড ফাংশনে ম্যাপ করা হয়েছে , যা দেখতে এটির মতো দেখাচ্ছে:

FCIMPL1(INT32, ObjectNative::GetHashCode, Object* obj) {  
    CONTRACTL  
    {  
        THROWS;  
        DISABLED(GC_NOTRIGGER);  
        INJECT_FAULT(FCThrow(kOutOfMemoryException););  
        MODE_COOPERATIVE;  
        SO_TOLERANT;  
    }  
    CONTRACTL_END;  

    VALIDATEOBJECTREF(obj);  

    DWORD idx = 0;  

    if (obj == 0)  
        return 0;  

    OBJECTREF objRef(obj);  

    HELPER_METHOD_FRAME_BEGIN_RET_1(objRef);        // Set up a frame  

    idx = GetHashCodeEx(OBJECTREFToObject(objRef));  

    HELPER_METHOD_FRAME_END();  

    return idx;  
}  
FCIMPLEND

গেটহ্যাশকোডেক্সের সম্পূর্ণ বাস্তবায়ন মোটামুটি বড়, সুতরাং কেবলমাত্র সি ++ উত্স কোডের সাথে লিঙ্ক করা সহজ ।


5
সেই ডকুমেন্টেশনের উদ্ধৃতি অবশ্যই খুব প্রাথমিক সংস্করণ থেকে এসেছে। এটি এখনকার এমএসডিএন নিবন্ধগুলিতে আর লেখা হয় না, সম্ভবত এটি বেশ ভুল।
হ্যানস প্যাস্যান্ট

4
তারা শব্দটি পরিবর্তিত করেছিল, হ্যাঁ, তবে এটি এখনও মূলত একই জিনিসটি বলে: "ফলস্বরূপ, এই পদ্ধতির ডিফল্ট বাস্তবায়ন হ্যাশিংয়ের উদ্দেশ্যে কোনও অনন্য অবজেক্ট সনাক্তকারী হিসাবে ব্যবহার করা উচিত নয়" "
ডেভিড ব্রাউন

7
ডকুমেন্টেশন দাবি করে যে বাস্তবায়ন হ্যাশিংয়ের জন্য বিশেষভাবে কার্যকর নয়? যদি কোনও বস্তু নিজেই সমান হয় এবং অন্য কিছুই না হয় তবে কোনও হ্যাশ কোড পদ্ধতি যা প্রদত্ত বস্তুর উদাহরণের জন্য সর্বদা একই মান প্রদান করবে এবং সাধারণত বিভিন্ন দৃষ্টান্তের জন্য বিভিন্ন মান প্রদান করবে, সমস্যা কী?
সুপারক্যাট

3
@ ta.speot.is: আপনি যা চান তা যদি নির্ধারণ করা হয় যে কোনও বিশেষ উদাহরণ ইতিমধ্যে অভিধানে যুক্ত হয়েছে কিনা , তবে রেফারেন্সের সাম্যতা নিখুঁত। স্ট্রিংগুলির সাথে, যেমন আপনি নোট করেছেন, একজন সাধারণত একই অক্ষরের একই ক্রম যুক্ত স্ট্রিংটি যুক্ত করা হয়েছে কিনা সে সম্পর্কে সাধারণত আগ্রহী । সে কারণেই stringওভাররাইড হয় GetHashCode। অন্যদিকে, ধরুন আপনি বিভিন্ন নিয়ন্ত্রণ প্রক্রিয়া Paintইভেন্টগুলিকে কতবার গণনা রাখতে চান । আপনি একটি ব্যবহার করতে পারেন Dictionary<Object, int[]>(প্রতিটি int[]সঞ্চিত হ'ল একটি আইটেম ধারণ করবে)।
সুপারক্যাট

6
@ It'sNotALie। তারপরে আর্কাইভ.আর.কে একটি অনুলিপি থাকার জন্য ধন্যবাদ ;-)
রবিআইআইআই

88

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

সাম্যকে ওভাররাইড করার সময়, আপনার সর্বদা একটি মিল থাকা উচিত Equals()এবং GetHashCode()(উদাহরণস্বরূপ দুটি মানের জন্য, যদি Equals()সত্য হয় তবে তাদের অবশ্যই একই হ্যাশ-কোডটি ফেরত দিতে হবে , তবে রূপান্তরটি প্রয়োজন হয় না ) - এবং এটি প্রদান ==/ !=অপারেটর সরবরাহ করাও সাধারণ এবং প্রায়শই বাস্তবায়ন IEquatable<T>

হ্যাশ কোড উত্পন্ন করার জন্য, একটি ফ্যাক্টরড যোগফল ব্যবহার করা সাধারণ, কারণ এটি যুক্ত করা মানগুলিতে সংঘর্ষ এড়ানো যায় - উদাহরণস্বরূপ, বেসিক 2 ফিল্ড হ্যাশের জন্য:

unchecked // disable overflow, for the unlikely possibility that you
{         // are compiling with overflow-checking enabled
    int hash = 27;
    hash = (13 * hash) + field1.GetHashCode();
    hash = (13 * hash) + field2.GetHashCode();
    return hash;
}

এটির সুবিধাটি হ'ল:

  • {1,2} এর হ্যাশ {2,1} এর হ্যাশের মতো নয়
  • {1,1} এর হ্যাশ {2,2} এর হ্যাশের মতো নয়

ইত্যাদি - যা কেবলমাত্র একটি অলক্ষিত যোগ, বা xor ( ^) ইত্যাদি ব্যবহার করে সাধারণ হতে পারে


ফ্যাক্টরেটেড-যোগফলের উপকার সম্পর্কে দুর্দান্ত পয়েন্ট; কিছু আমি আগে বুঝতে পারি না!
লুফোল

কল্পিত যোগফল (উপরে লেখা হিসাবে) মাঝে মাঝে ওভারফ্লো ব্যতিক্রম ঘটায় না?
সিনায়লা

4
@ সিনলাও হ্যাঁ, এটি করা উচিত unchecked। ভাগ্যক্রমে, uncheckedসি # এ ডিফল্ট, তবে এটি সুস্পষ্ট করা আরও ভাল হবে; সম্পাদিত
মার্ক গ্রাভেল

7

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

মৌলিক ধরনের তথ্য চান byte, short, int, long, charএবং stringএকটি ভাল GetHashCode পদ্ধতি বাস্তবায়ন। কিছু অন্যান্য শ্রেণি এবং কাঠামো, Pointউদাহরণস্বরূপ, GetHashCodeএমন একটি পদ্ধতি প্রয়োগ করে যা আপনার নির্দিষ্ট প্রয়োজনের জন্য উপযুক্ত বা নাও হতে পারে। এটি যথেষ্ট ভাল কিনা তা দেখার জন্য আপনাকে কেবল এটি চেষ্টা করে দেখতে হবে।

প্রতিটি শ্রেণি বা কাঠামোর জন্য ডকুমেন্টেশন আপনাকে বলতে পারে এটি ডিফল্ট বাস্তবায়নকে ওভাররাইড করে কিনা। যদি এটি ওভাররাইড না করে তবে আপনার নিজের প্রয়োগটি ব্যবহার করা উচিত। যে GetHashCodeপদ্ধতিতে আপনার নিজেকে তৈরি করা উচিত সেখানে যে কোনও ক্লাস বা স্ট্রাক্টের জন্য আপনার নিজের প্রয়োগ করা উচিত যা হ্যাশ কোড গণনা করতে উপযুক্ত সদস্যদের ব্যবহার করে।


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

@ মার্ক কঙ্কর: এটি অবশ্যই বলার অর্থ ছিল না। আমি শেষ অনুচ্ছেদটি সামঞ্জস্য করব। :)
গুফা

বেসিক ডেটা টাইপগুলি অন্ততপক্ষে আমার ক্ষেত্রে একটি ভাল গেটহ্যাশকোড পদ্ধতি প্রয়োগ করে না। উদাহরণস্বরূপ, গেটহ্যাশকোড int এর জন্য নম্বরটি নিজেই ফেরত দেয়: (123) .গেটহ্যাশকোড () 123 প্রদান করে
fdermishin

5
@ ব্যবহারকারী 502144 এবং এতে কী দোষ আছে? এটি একটি নিখুঁত অনন্য শনাক্তকারী যা গণনা করা সহজ, সমতার বিষয়ে কোনও মিথ্যা
ধনাত্মকতা

@ রিচার্ড রাস্ট: হ্যাশটেবল ব্যবহার করার সময় কীগুলি খারাপভাবে বিতরণ করা যায় তা ঠিক আছে। : এই উত্তর কটাক্ষপাত stackoverflow.com/a/1388329/502144
fdermishin

5

যেহেতু আমি এমন কোনও উত্তর খুঁজে পাইনি যা ব্যাখ্যা করে যে আমাদের কেন ওভাররাইড করা উচিত GetHashCodeএবং Equalsকাস্টম স্ট্রাক্টগুলির জন্য এবং কেন ডিফল্ট রূপায়ণ "হ্যাশ টেবিলের কী হিসাবে ব্যবহারের পক্ষে উপযুক্ত নয়", তাই আমি এই ব্লগে একটি লিঙ্ক রেখে যাব পোস্ট , যা ব্যাখ্যা করে যে কেন ঘটেছে সমস্যার বাস্তব ক্ষেত্রে উদাহরণ সহ।

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

স্ট্রাইকগুলির জন্য ডিফল্ট হ্যাশ কারণ ধীর এবং খুব ভাল নয়:

সিএলআরটি যেভাবে ডিজাইন করা হয়েছে, প্রতিটি সদস্যের কাছে সংজ্ঞায়িত System.ValueTypeবা কলগুলিতে কল করা System.Enum[ বক্সিং ] বক্সিং বক্সিং বরাদ্দের কারণ হতে পারে [...]

হ্যাশ ফাংশনের একজন প্রয়োগকারী একটি দ্বিধাদ্বন্দ্বের মুখোমুখি হন: হ্যাশ ফাংশনটির একটি ভাল বিতরণ করুন বা এটি দ্রুত তৈরি করুন। কিছু কিছু ক্ষেত্রে, এটা তাদের উভয় অর্জন করা সম্ভব, কিন্তু এটা জেনেরিক এই কাজ করতে হার্ড মধ্যে ValueType.GetHashCode

স্ট্রাক্টের ক্যানোনিকাল হ্যাশ ফাংশন সমস্ত ক্ষেত্রের "সংযুক্ত" হ্যাশ কোডগুলি। তবে কোনও ValueTypeপদ্ধতিতে ক্ষেত্রের একটি হ্যাশ কোড পাওয়ার একমাত্র উপায় হ'ল প্রতিচ্ছবি ব্যবহার করা । সুতরাং, সিএলআর লেখকগণ বিতরণে গতি নিয়ে বাণিজ্য করার সিদ্ধান্ত নিয়েছে এবং ডিফল্ট GetHashCodeসংস্করণটি কেবল প্রথম নন-ফিল্ডের একটি হ্যাশ কোড দেয় এবং এটি টাইপ আইডি দিয়ে "মুঙ্গ করে" [...] এটি যুক্তিসঙ্গত আচরণ হয় যদি না এটি হয় । উদাহরণস্বরূপ, যদি আপনি যথেষ্ট দুর্ভাগ্য হন এবং আপনার কাঠামোর প্রথম ক্ষেত্রটির বেশিরভাগ ক্ষেত্রে একই মান থাকে তবে একটি হ্যাশ ফাংশন সর্বদা একই ফলাফল সরবরাহ করবে । এবং, আপনি যেমন কল্পনা করতে পারেন, যদি এই দৃষ্টান্তগুলি হ্যাশ সেট বা হ্যাশ টেবিলের মধ্যে সংরক্ষণ করা হয় তবে এটি কঠোর পারফরম্যান্সের প্রভাব ফেলবে।

[...] প্রতিবিম্ব-ভিত্তিক বাস্তবায়ন ধীর । খুব ধীর.

[...] উভয় ValueType.Equalsএবং ValueType.GetHashCodeএকটি বিশেষ অপ্টিমাইজেশন রয়েছে। যদি কোনও ধরণের "পয়েন্টার" না থাকে এবং সঠিকভাবে প্যাক করা থাকে [...] তবে আরও সর্বোত্তম সংস্করণ ব্যবহৃত হয়: GetHashCodeএকটি উদাহরণের সাথে পুনরাবৃত্তি হয় এবং 4 বাইট এবং Equalsপদ্ধতির XORs ব্লক ব্যবহার করে দুটি দৃষ্টান্তের তুলনা করে memcmp। [...] তবে অপ্টিমাইজেশনটি খুব জটিল। প্রথমত, অপ্টিমাইজেশন সক্ষম করা কবে হবে তা জানা শক্ত [...] দ্বিতীয়ত, একটি মেমরি তুলনা অগত্যা আপনাকে সঠিক ফলাফল দেয় না । এখানে একটি সহজ উদাহরণ: [...] -0.0এবং +0.0সমান তবে বিভিন্ন বাইনারি উপস্থাপনা রয়েছে।

পোস্টে বর্ণিত রিয়েল-ওয়ার্ল্ড ইস্যু:

private readonly HashSet<(ErrorLocation, int)> _locationsWithHitCount;
readonly struct ErrorLocation
{
    // Empty almost all the time
    public string OptionalDescription { get; }
    public string Path { get; }
    public int Position { get; }
}

আমরা একটি টিউপল ব্যবহার করেছি যাতে ডিফল্ট সমতা প্রয়োগের সাথে একটি কাস্টম স্ট্রাক্ট থাকে। এবং দুর্ভাগ্যক্রমে, কাঠামোর একটি alচ্ছিক প্রথম ক্ষেত্র ছিল যা প্রায় সর্বদা [খালি স্ট্রিং] এর সমান । কয়েক হাজার আইটেম দিয়ে সংগ্রহ শুরু করার জন্য কয়েক মিনিট সময় নিলে সেটটিতে উপাদানগুলির সংখ্যা উল্লেখযোগ্যভাবে বৃদ্ধি পেয়ে একটি বাস্তব পারফরম্যান্স সমস্যার কারণ হয় until

সুতরাং, "আমার নিজের ক্ষেত্রে কী কী প্যাক করা উচিত এবং কোন ক্ষেত্রে আমি নিরাপদে ডিফল্ট বাস্তবায়নের উপর নির্ভর করতে পারি" এই প্রশ্নের উত্তর দেওয়ার জন্য, কমপক্ষে স্ট্রাক্টের ক্ষেত্রে আপনাকে ওভাররাইড করা উচিত Equalsএবং GetHashCodeযখনই আপনার কাস্টম স্ট্রাক্ট হিসাবে ব্যবহার করা যেতে পারে একটি হ্যাশ টেবিল বা Dictionary। বক্সিং এড়ানোর জন্য
আমি IEquatable<T>এই ক্ষেত্রে প্রয়োগেরও সুপারিশ করব ।

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


1

সাধারণভাবে বলতে গেলে, আপনি যদি সমানকে ওভাররাইড করছেন তবে আপনি গেটহ্যাশকোডকে ওভাররাইড করতে চান। এর কারণ হ'ল উভয়ই আপনার শ্রেণি / কাঠামোর সাম্যের তুলনা করতে ব্যবহৃত হয়।

ফু এ, বি পরীক্ষা করার সময় সমান ব্যবহার করা হয়;

যদি (এ == খ)

যেহেতু আমরা জানি যে পয়েন্টারটি মেলে সম্ভবত না, তাই আমরা অভ্যন্তরীণ সদস্যদের তুলনা করতে পারি।

Equals(obj o)
{
    if (o == null) return false;
    MyType Foo = o as MyType;
    if (Foo == null) return false;
    if (Foo.Prop1 != this.Prop1) return false;

    return Foo.Prop2 == this.Prop2;
}

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

আমি সাধারণত করি,

GetHashCode()
{
    int HashCode = this.GetType().ToString().GetHashCode();
    HashCode ^= this.Prop1.GetHashCode();
    etc.

    return HashCode;
}

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

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


2
H = পদ্ধতির কোনও হ্যাশ তৈরির জন্য বিশেষত ভাল দৃষ্টিভঙ্গি নয় - এটি প্রচুর প্রচলিত / অনুমানযোগ্য সংঘর্ষের দিকে ঝোঁক দেয় - উদাহরণস্বরূপ যদি প্রোপ 1 = প্রোপ 2 = 3
মার্ক গ্র্যাভেল

মানগুলি একই হলে, বস্তু সমান হওয়ায় সংঘর্ষে আমি কোনও সমস্যা দেখছি না। 13 * হ্যাশ + নিউহ্যাশ যদিও আকর্ষণীয় বলে মনে হচ্ছে।
বেনিট ডিল

2
বেন: এটি Obj1 {প্রোপ 1 = 12, প্রোপ 2 = 12} এবং ওবজে 2 {প্রোপ 1 = 13, প্রোপ 2 = 13}
টোম কাফকা

0

আপনি যদি কেবল পোকো নিয়ে কাজ করছেন তবে আপনি আপনার জীবনকে কিছুটা সহজ করার জন্য এই ইউটিলিটিটি ব্যবহার করতে পারেন:

var hash = HashCodeUtil.GetHashCode(
           poco.Field1,
           poco.Field2,
           ...,
           poco.FieldN);

...

public static class HashCodeUtil
{
    public static int GetHashCode(params object[] objects)
    {
        int hash = 13;

        foreach (var obj in objects)
        {
            hash = (hash * 7) + (!ReferenceEquals(null, obj) ? obj.GetHashCode() : 0);
        }

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