GetHashCode ඉක්මවා යාම සඳහා හොඳම ඇල්ගොරිතම කුමක්ද?


1461

.NET හි, මෙම GetHashCodeක්‍රමය .NET මූලික පන්ති පුස්තකාල පුරා බොහෝ ස්ථානවල භාවිතා වේ. එය නිසි ලෙස ක්‍රියාත්මක කිරීම විශේෂයෙන් වැදගත් වන්නේ එකතුවක ඇති අයිතම ඉක්මනින් සොයා ගැනීම හෝ සමානාත්මතාවය තීරණය කිරීමේදී ය.

GetHashCodeමගේ අභිරුචි පංති සඳහා ක්‍රියාත්මක කරන්නේ කෙසේද යන්න පිළිබඳ සම්මත ඇල්ගොරිතමයක් හෝ හොඳම භාවිතයක් තිබේද?


38
මෙම ප්‍රශ්නය සහ පහත ලිපිය කියවීමෙන් පසු, මට ඉක්මවා යාම ක්‍රියාත්මක කළ හැකිය GetHashCode. එය අන් අයට ප්‍රයෝජනවත් වේ යැයි මම බලාපොරොත්තු වෙමි. එරික් Lippert විසින් ලියන GetHashCode සඳහා මාර්ගෝපදේශ හා නීති
Rene

4
"හෝ සමානාත්මතාවය තීරණය කිරීම": නැත! එකම හැෂ් කේතය ඇති වස්තු දෙකක් අනිවාර්යයෙන්ම සමාන නොවේ.
තෝමස් ලෙවෙස්ක්

1
HThomasLevesque ඔබ හරි, එකම හැෂ් කේතය ඇති වස්තු දෙකක් අනිවාර්යයෙන්ම සමාන නොවේ. නමුත් තවමත් GetHashCode()බොහෝ ක්‍රියාත්මක කිරීම් වලදී භාවිතා වේ Equals(). ඒක තමයි මම ඒ ප්‍රකාශයෙන් අදහස් කළේ. GetHashCode()ඇතුළත Equals()බොහෝ විට අසමානතාවය තීරණය කිරීම සඳහා කෙටිමඟක් ලෙස භාවිතා වේ , මන්ද වස්තූන් දෙකකට වෙනස් හැෂ් කේතයක් තිබේ නම් ඒවා සමාන නොවන වස්තූන් විය යුතු අතර ඉතිරි සමානාත්මතා පරීක්‍ෂණය ක්‍රියාත්මක කළ යුතු නොවේ.
bitbonk

3
@bitbonk සාමාන්‍යයෙන්, දෙකම GetHashCode()සහ Equals()වස්තූන්ගේ සියලු ක්ෂේත්‍ර දෙස බැලිය යුතුය (හැෂ්කෝඩ් සමාන හෝ පරීක්ෂා නොකළේ නම් සමානව මෙය කළ යුතුය). මේ නිසා, GetHashCode()ඇතුළට ඇමතුමක් Equals()බොහෝ විට අතිරික්ත වන අතර කාර්ය සාධනය අඩු කළ හැකිය. Equals()කෙටි පරිපථයකටද එය වේගවත් කළ හැකිය - කෙසේ වෙතත් සමහර අවස්ථාවලදී හැෂ් කේත හැඹිලිගත කර GetHashCode()චෙක්පත වේගවත් හා වටිනවා. වැඩි විස්තර සඳහා මෙම ප්‍රශ්නය බලන්න .
NotEnoughData

2020 ජනවාරි යාවත්කාලීන කරන්න: එරික් ලිපර්ට්ගේ බ්ලොග් අඩවිය පිහිටා ඇත්තේ: docs.microsoft.com/en-us/archive/blogs/ericlippert/…
රික් ඩේවින්

Answers:


1614

මම සාමාන්යයෙන් ජොශ් Bloch ගණන්වල දී ක්රියාත්මක කිරීම වගේ දෙයක් එක්ක යන්න මනස්කාන්ත ඵලදායි ජාවා . එය වේගවත් වන අතර ගැටුම් ඇතිවීමට ඉඩක් නැති හොඳ හැෂ් එකක් නිර්මාණය කරයි. වෙනස් ප්‍රාථමික අංක දෙකක් තෝරන්න, උදා: 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 හොඳයි ... මම කුඩා සංඛ්‍යා සමඟ දැක ඇති බොහෝ උදාහරණ ප්‍රාථමිකයන් භාවිතා කිරීමට නැඹුරු වුවද, ප්‍රයිම් නොවන සංඛ්‍යා බොහෝ විට භාවිතා කරන අවම වශයෙන් සමාන ඇල්ගොරිතම ඇත. මෙම-quite- නො FNV උදාහරණයක් පසු, උදාහරණයක් ලෙස, මම පැහැදිලිවම වැඩ කරන සංඛ්යා භාවිත කර - එහෙත් ආරම්භක වටිනාකම අගමැති නොවේ. (ගුණ කිරීමේ නියතය ප්‍රමුඛ වුවත් එය කොතරම් වැදගත්දැයි මම නොදනිමි.)

XORප්‍රධාන හේතු දෙකක් නිසා ඉංග්‍රීසි හැෂ්කෝඩ් භාවිතයට වඩා මෙය හොඳය . අපට intක්ෂේත්‍ර දෙකක් සහිත වර්ගයක් ඇතැයි සිතමු :

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

මාර්ගය වන විට, කලින් ඇල්ගොරිතම යනු C # සම්පාදකයා විසින් නිර්නාමික වර්ග සඳහා දැනට භාවිතා කිරීමයි.

මෙම පිටුව විකල්ප කිහිපයක් ලබා දෙයි. මම හිතන්නේ බොහෝ අවස්ථාවන්හිදී ඉහත සඳහන් දෑ “ප්‍රමාණවත්” වන අතර එය මතක තබා ගැනීම සහ නිවැරදි කර ගැනීම ඇදහිය නොහැකි තරම් පහසුය. මෙම FNV විකල්ප සමාන සරල, නමුත් වෙනස් නියත භාවිතා හා XORවෙනුවට ADDසංයුක්ත මෙහෙයුමක් ලෙස. එය හැඟුනේ දෙයක් පහත කේතය වැනි, නමුත් සාමාන්ය FNV ඇල්ගොරිතමය තනි බයිට් මත ක්රියා කරන අතර, ඒ නිසා මෙම වෙනුවට 32-bit පූරක අගය ක, බයිට එක් ප්රතිඵලයක්ම ඉටු කිරීමට, සංශෝධනය කිරීම අවශ්ය වනු ඇත. එෆ්එන්වී ද විචල්ය දත්ත සඳහා නිර්මාණය කර ඇති අතර අප මෙහි භාවිතා කරන ආකාරය සෑම විටම එකම ක්ෂේත්‍ර අගයන් සඳහා වේ. මෙම පිළිතුර පිළිබඳ අදහස් දැක්වෙන්නේ ඉහත කේතය සත්‍ය වශයෙන්ම ක්‍රියා නොකරන බවයි (පරීක්ෂා කරන ලද නියැදි නඩුවේදී) ඉහත එකතු කිරීමේ ප්‍රවේශය ලෙස ය.

// 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 අභිබවා යා හැකිය. පොදුවේ ගත් කල, විකෘති යොමු වර්ග සඳහා, ඔබ GetHashCode අභිබවා යා යුත්තේ නම් පමණි:

  • විකෘති නොවන ක්ෂේත්‍ර වලින් ඔබට හැෂ් කේතය ගණනය කළ හැකිය; හෝ
  • විකෘති වස්තුවක හැෂ් කේතය වෙනස් නොවන බවට ඔබට සහතික විය හැකිය, වස්තුව එහි හැෂ් කේතය මත රඳා පවතින එකතුවක අඩංගු වේ.

8
ඔබ සඳහන් කරන පොතේ විස්තර කර ඇති ඇල්ගොරිතම තව ටිකක් සවිස්තරාත්මක වන අතර එය විවිධ දත්ත වර්ග සඳහා කුමක් කළ යුතුද යන්න පැහැදිලිව විස්තර කරයි. උදා: GetHashcode ඇමතීම වෙනුවට දිගු කාලීන (int) (ක්ෂේත්‍ර ^ f >>> 32) ක්ෂේත්‍ර සඳහා. Long.GetHashCodes ඒ ආකාරයෙන් ක්‍රියාත්මක වේද?
bitbonk

13
ඔව්, Int64.GetHashCode එය හරියටම කරයි. ජාවා හි බොක්සිං අවශ්‍ය වනු ඇත. එය මට මතක් කර දෙයි - පොතට සබැඳියක් එක් කිරීමට කාලයයි ...
ජෝන් ස්කීට්

77
23 හොඳ තේරීමක් නොවේ, (.net 3.5 SP1 අනුව) Dictionary<TKey,TValue>හොඳ බෙදාහැරීමේ මොඩියුලයක් ඇතැම් ප්‍රාථමිකයන් උපකල්පනය කරයි. 23 ඒවායින් එකකි. එබැවින් ඔබට ධාරිතාව 23 සහිත ශබ්ද කෝෂයක් තිබේ නම් GetHashCode, සංයෝග හැෂ් කේතයට බලපෑම් කිරීමට අවසාන දායකත්වය පමණි . එබැවින් මම 23 වෙනුවට 29 භාවිතා කිරීමට කැමැත්තෙමි.
CodesInChaos

23
OdeCodeInChaos: බාල්දියට බලපෑම් කරන්නේ අවසාන දායකත්වය පමණි - එබැවින් එය නරකම ලෙස ශබ්ද කෝෂයේ ඇතුළත් කිරීම් 23 ම සොයා බැලිය යුතුය. එය තවමත් සෑම ප්‍රවේශයකම සැබෑ හැෂ් කේතය පරීක්ෂා කිරීමට යන්නේ එය ලාභදායී වනු ඇත. ඔබට එතරම් කුඩා ශබ්ද කෝෂයක් තිබේ නම්, එය එතරම් වැදගත් නොවනු ඇත.
ජෝන් ස්කීට්

20
Aj වජ්ඩා: මම සාමාන්‍යයෙන් 0 සඳහා has ලදායී හැෂ් කේතය ලෙස භාවිතා කරමි null- එය ක්ෂේත්‍රය නොසලකා හැරීමට සමාන නොවේ.
ජෝන් ස්කීට්

435

ValueTuple - C # 7 සඳහා යාවත්කාලීන කිරීම

@Cactuaroid අදහස් දැක්වීමේදී සඳහන් කළ පරිදි, වටිනාකමක් භාවිතා කළ හැකිය. මෙය යතුරු එබීම් කිහිපයක් ඉතිරි කරන අතර වඩා වැදගත් වන්නේ තොගය මත පමණක් ක්‍රියාත්මක කිරීමයි (කුණු කසළ නැත):

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

. tuple විකල්පය වඩා උසස් විය යුතුය.)

නිර්නාමික වර්ගය (මුල් පිළිතුර)

මයික්‍රොසොෆ්ට් දැනටමත් හොඳ ජනක හැෂ්කෝඩ් උත්පාදක යන්ත්‍රයක් සපයයි: ඔබේ දේපල / ක්ෂේත්‍ර අගයන් නිර්නාමික වර්ගයකට පිටපත් කර එය හෑෂ් කරන්න:

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

මෙය ඕනෑම දේපල ගණනක් සඳහා ක්‍රියා කරනු ඇත. එය බොක්සිං භාවිතා නොකරයි. එය නිර්නාමික වර්ග සඳහා රාමුව තුළ දැනටමත් ක්‍රියාත්මක කර ඇති ඇල්ගොරිතම භාවිතා කරයි.


85
ඔව්, නිර්නාමික GetHashCodeක්‍රියාත්මක කිරීම ඉතා effective ලදායී වේ (BTW එය ජෝන් ස්කීට්ගේ පිළිතුරට සමාන ය), නමුත් මෙම විසඳුමේ ඇති එකම ගැටළුව නම් ඔබ ඕනෑම GetHashCodeඇමතුමකින් නව අවස්ථාවක් ජනනය කිරීමයි . විශාල හැෂ් එකතු සඳහා දැඩි ප්‍රවේශයක් ඇති විට එය ටිකක් ඉහළින් විය හැකිය ...
digEmAll

5
igdigEmAll හොඳ කරුණක් නම්, නව වස්තුවක් නිර්මාණය කිරීමේ පොදු කාර්යය ගැන මම නොසිතුවෙමි. ජෝන් ස්කීට්ගේ පිළිතුර වඩාත් කාර්යක්ෂම වන අතර බොක්සිං භාවිතා නොකරනු ඇත. (Umb කුම්බා VB හි සලකුණු නොකල විසඳුම සඳහා, Int64 (දිගු) භාවිතා කර ගණනය කිරීම් වලින් පසුව එය කපා දමන්න.)
රික් ලව්

42
කියන්න පුළුවන් new { PropA, PropB, PropC, PropD }.GetHashCode()
sehe

17
VB.NET නිර්නාමික ආකාරයේ නිර්මාණය සඳහා යතුර භාවිතා කළ යුතුය: New With {Key PropA}.GetHashCode()එසේ නොමැතිනම් GetHashCode එකම 'හඳුනාගැනීමේ' ගුණාංග සහිත විවිධ වස්තු සඳහා එකම හැෂ් කේතය ලබා නොදේ.
ඩේවිඩ් ඔස්බෝන්

4
Eth එම අවස්ථාවේ දී, හැෂ් කේතය ගණනය කරන සෑම අවස්ථාවකම ගණනය කිරීම වෙනුවට IEnumerable ලැයිස්තු වටිනාකමක් ලෙස කොතැනක හෝ සුරැකීම සලකා බලමි. GetHashCode තුළ සෑම අවස්ථාවකදීම ToList ගණනය කිරීම බොහෝ අවස්ථාවන්හි කාර්ය සාධනයට හානියක් විය හැකිය.
රික් ලව්

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>
nawfal

5
ඔබට එම ක්‍රම ප්‍රතික්‍රියා කර මූලික තර්කනය එක් ශ්‍රිතයකට සීමා කළ හැකිය
nawfal

12
අහඹු ලෙස, 31 යනු CPU හි මාරුවීමක් හා අඩු කිරීමක් වන අතර එය ඉතා වේගවත් වේ.
චුයි ටේ

4
@nightcoder ඔබට පරාමිති භාවිතා කළ හැකිය .
ANeves

6
H චුයිටේ මෙය සියලුම මර්සෙන් ප්‍රයිම්ස් වලට පොදු දෙයක්.
ෆරාප්

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
"OverflowException අල්ලා ගැනීමෙන් පසුව වැඩි දියුණු කළ හැකිය" මෙහි සමස්ත කරුණ uncheckedවන්නේ අපේක්ෂිත පිටාර ගැලීම් වල ව්‍යතිරේකයන් වළක්වා ගැනීමයි GetHashCode. එබැවින් අගය පිරී ඉතිරී යන intඅතර එය කිසිසේත්ම හානියක් නොවේ නම් එය වැරදිය .
ටිම් ෂ්මෙල්ටර්

1
මෙම ඇල්ගොරිතමයේ ඇති එක් ගැටළුවක් නම්, ඕනෑම දිගක් ශුන්‍යයෙන් පිරී ඇති අතර, එහි දිග නොතකා සෑම විටම 0 නැවත පැමිණේ
නේතන් ඇඩම්ස්

2
මෙම සහායක ක්‍රමය මඟින් නව වස්තුවක් වෙන් කරයි []
ජේම්ස් නිව්ටන්-කිං

1
Athan නාතන් ඇඩම්ස් සඳහන් කරන පරිදි, 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);
}

System.Int32 සඳහා දිගු කිරීමේ ක්‍රමයක් ලිවීමෙන් වැළකී සිටීමට ඔබට අවශ්‍ය නම්:

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>.Defaultgetter දැන් JIT සහජයෙන්ම - අදින්න ඉල්ලීම මෙම බ්ලොග් සටහනේ ස්ටීවන් ටූබ් විසින් සඳහන් කර ඇත.


1
මම තෘතීයික ක්‍රියාකරු සමඟ ඇති රේඛාව වෙනස් කරමි:var h = Equals(obj, default(T)) ? 0 : obj.GetHashCode();
බිල් බැරී

ත්‍රිමාණ ක්‍රියාකරු obj != nullවිසින් boxඋපදෙස් වර්ගයක් සම්පාදනය කරනු ඇති අතර Tඑය අගය වර්ගයක් නම් මතකය වෙන් කරනු ඇත . ඒ වෙනුවට ඔබට ක්‍රමයේ obj.Equals(null)අථත්‍ය ඇමතුමකට සම්පාදනය වන භාවිතා කළ හැකිය Equals.
මාටින් ලිවර්සේජ්

මොකද this.hashCode != h. එය එකම අගයක් ලබා නොදේ.
Şafak Gür

කණගාටුයි, මගේ අදහස සංස්කරණය කරනවා වෙනුවට එය ඉවත් කිරීමට කළමනාකරණය කරන්න. නව ව්‍යුහයක් නිර්මාණය කිරීම වඩා ප්‍රයෝජනවත්ද? පසුව හැෂ් කේතය කියවීමට නොහැකි ලෙස වෙනස් කර කරන්න: "සලකුණු නොකළ {this.hashCode ^ = h * 397; this මෙය ආපසු ලබා දෙන්න;" උදාහරණයක් වශයෙන්?
එරික් කාල්සන්

නිශ්චලතාවයට එහි ප්‍රතිලාභ ඇත ( විකෘති ව්‍යුහයන් නපුරු වන්නේ ඇයි? ). කාර්ය සාධනය ගැන, ගොඩවල් වල කිසිදු ඉඩක් වෙන් නොකරන බැවින් මා කරන දෙය බෙහෙවින් ලාභදායී වේ.
Şafak Gür

35

.NET සම්මත 2.1 සහ ඊට ඉහළින්

ඔබ .NET සම්මත 2.1 හෝ ඊට වැඩි භාවිතා කරන්නේ නම්, ඔබට System.HashCode struct භාවිතා කළ හැකිය . එය භාවිතා කිරීමේ ක්‍රම දෙකක් තිබේ:

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();
}

GetHashCode පහසුයි

වැඩි විස්තර සහ අදහස් සඳහා ඔබට ' GetHashCode Made Easy ' යන සම්පූර්ණ බ්ලොග් සටහන කියවිය හැකිය .

භාවිත උදාහරණය

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;
    }
}

හොඳ ඇල්ගොරිතමයක් වන්නේ කුමක් ද?

කාර්ය සාධනය

හැෂ් කේතයක් ගණනය කරන ඇල්ගොරිතම වේගවත් විය යුතුය. සරල ඇල්ගොරිතමයක් සාමාන්‍යයෙන් වේගවත් එකක් වනු ඇත. අමතර මතකයක් වෙන් නොකරන එකක් කසළ එකතු කිරීමේ අවශ්‍යතාවය ද අඩු කරනු ඇති අතර එමඟින් කාර්ය සාධනය වැඩි දියුණු වේ.

නිර්ණායක

හැෂිං ඇල්ගොරිතම නිර්ණායක විය යුතුය, එනම් එකම ආදානය ලබා දීමෙන් එය සෑම විටම එකම ප්‍රතිදානය නිපදවිය යුතුය.

Isions ට්ටන අඩු කරන්න

හැෂ් කේතයක් ගණනය කරන ඇල්ගොරිතමයට හැෂ් isions ට්ටන අවම අගයක් තබා ගත යුතුය. හැෂ් ision ට්ටනය යනු GetHashCodeවිවිධ වස්තූන් දෙකකට ඇමතුම් දෙකක් සමාන හැෂ් කේත නිපදවන විට ඇතිවන තත්වයකි . Isions ට්ටනයට ඉඩ දී ඇති බව සලකන්න (සමහරුන්ට ඒවා නැතැයි වැරදි වැටහීමක් ඇත) නමුත් ඒවා අවම වශයෙන් තබා ගත යුතුය.

හොඳ හැෂ් ශ්‍රිතයක් මඟින් අපේක්ෂිත යෙදවුම් එහි නිමැවුම් පරාසය පුරා හැකි තරම් ඒකාකාරව සිතියම් ගත කළ යුතුය. එයට ඒකාකාරිත්වයක් තිබිය යුතුය.

වැළැක්වීමේ DoS

.NET Core හි ඔබ යෙදුමක් නැවත ආරම්භ කරන සෑම අවස්ථාවකම ඔබට විවිධ හැෂ් කේත ලැබෙනු ඇත. සේවා ප්‍රතික්ෂේප කිරීම වැළැක්වීම සඳහා මෙය ආරක්ෂක ලක්ෂණයකි. .NET Framework සඳහා පහත දැක්වෙන App.config ගොනුව එක් කිරීමෙන් ඔබ මෙම අංගය සක්‍රීය කළ යුතුය :

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

මෙම අංගය නිසා, හැෂ් කේත ඒවා නිර්මාණය කරන ලද යෙදුම් වසමෙන් පිටත කිසි විටෙකත් භාවිතා නොකළ යුතුය, ඒවා කිසි විටෙක එකතුවක ප්‍රධාන ක්ෂේත්‍ර ලෙස භාවිතා නොකළ යුතු අතර ඒවා කිසි විටෙකත් නොනැසී පැවතිය යුතුය.

මේ ගැන වැඩි විස්තර මෙතැනින් කියවන්න .

ගුප්ත විද්‍යාත්මකව ආරක්ෂිතද?

ඇල්ගොරිතම ගුප්ත ලේඛන හැෂ් ශ්‍රිතයක් විය යුතු නැත . එහි අර්ථය පහත සඳහන් කොන්දේසි සපුරාලිය යුතු නැත:

  • දී ඇති හැෂ් අගයක් ලබා දෙන පණිවිඩයක් ජනනය කිරීම කළ නොහැක්කකි
  • එකම හැෂ් අගය සහිත වෙනස් පණිවිඩ දෙකක් සොයා ගැනීම කළ නොහැක්කකි
  • පණිවිඩයක කුඩා වෙනසක් මගින් හැෂ් අගය එතරම් පුළුල් ලෙස වෙනස් කළ යුතු අතර නව හැෂ් අගය පැරණි හැෂ් අගය (හිම වලසුන්ගේ බලපෑම) සමඟ සම්බන්ධයක් නැති බව පෙනේ.

1
මෙය ඉතා හොඳ පිළිතුරකි. මීට අමතරව, ඔබට "වේගය" "කාර්ය සාධනය" ලෙස වෙනස් කිරීම සහ වෙන් කිරීමකින් තොරව දේපල එකතු කිරීම සලකා බැලිය හැකිය. සාදන ලද HashCodeවර්ගයද එය තෘප්තිමත් කරයි.
ටිමෝ

29

බොහෝ අවස්ථාවන්හිදී සමාන () බහු ක්ෂේත්‍ර සංසන්දනය කරන විට ඔබේ GetHash () එක් ක්ෂේත්‍රයක හෝ බොහෝ ක්ෂේත්‍රවල හැෂ් නම් එය ඇත්ත වශයෙන්ම වැදගත් නොවේ. හැෂ් ගණනය කිරීම ඇත්තෙන්ම ලාභදායී බව ඔබ සහතික කර ගත යුතුය ( ප්‍රතිපාදන නැත , කරුණාකර) සහ වේගවත් ( බර ගණනය කිරීම් සහ නිසැකවම දත්ත සමුදා සම්බන්ධතා නොමැත) සහ හොඳ බෙදාහැරීමක් සපයයි.

බර ඉසිලීම සමාන () ක්‍රමයේ කොටසක් විය යුතුය; හැෂ් යනු හැකි තරම් අයිතම කිහිපයක් මත සමාන () ඇමතීම සක්‍රීය කිරීම සඳහා ඉතා ලාභදායී මෙහෙයුමක් විය යුතුය.

එක් අවසාන ඉඟියක්: GetHashCode () බහු යෙදවුම් ධාවනය මත ස්ථාවර වීම මත රඳා නොසිටින්න . බොහෝ .නෙට් වර්ග නැවත ආරම්භ කිරීමෙන් පසුව ඔවුන්ගේ හැෂ් කේත එලෙසම පවතින බවට සහතික නොවේ, එබැවින් ඔබ භාවිතා කළ යුත්තේ මතක දත්ත ව්‍යුහයන් සඳහා GetHashCode () හි අගය පමණි.


10
"බොහෝ අවස්ථාවන්හිදී සමාන () බහු ක්ෂේත්‍ර සංසන්දනය කරන විට ඔබේ GetHash () එක් ක්ෂේත්‍රයක හෝ බොහෝ ක්ෂේත්‍රවල හැෂ් නම් එය ඇත්ත වශයෙන්ම වැදගත් නොවේ." මෙය භයානක උපදෙස් වේ, මන්ද යත්, නොකැඩූ ක්ෂේත්‍රවල පමණක් වෙනස් වන වස්තූන් සඳහා, ඔබට හැෂ් isions ට්ටන ලැබෙනු ඇත. මෙය නිතර සිදුවන්නේ නම්, හැෂ් මත පදනම් වූ එකතු කිරීම් (හැෂ්මැප්, හැෂ්සෙට් ආදිය) පිරිහීමට ලක් වේ (නරකම අවස්ථාවෙහිදී ඕ (එන්) දක්වා).
sleske

10
මෙය සැබවින්ම ජාවාහි සිදු විය: JDK String.hashCode () හි මුල් සංස්කරණ වලදී සලකා බලනු ලැබුවේ නූල්වල ආරම්භය පමණි; ඔබ හැෂ්මැප්ස් හි යතුරු ලෙස නූල් භාවිතා කළේ නම් එය අවසානයේ පමණක් වෙනස් වේ (මෙය URL සඳහා පොදු වේ). එබැවින් ඇල්ගොරිතම වෙනස් කරන ලදි (JDK 1.2 හෝ 1.3 හි මම විශ්වාස කරමි).
sleske

4
එම එක් ක්ෂේත්‍රයක් 'හොඳ බෙදාහැරීමක් සපයයි' (මගේ පිළිතුරේ අවසාන කොටස) නම්, එක් ක්ෂේත්‍රයක් ප්‍රමාණවත්ය .. එය හොඳ බෙදාහැරීමක් ලබා නොදෙන්නේ නම් (එවිට) ඔබට තවත් ගණනය කිරීමක් අවශ්‍ය වේ. (උදා: යන්තම් තවත් ක්ෂේත්ර භාවිතා කරන්නේ හොඳ බෙදාහැරීමට හෝ ක්ෂේත්ර කිහිපයක් භාවිතා කරන්න)
බර්ට් Huijben

GetHashCodeමතක විබෙදුම් සිදුකිරීමේ ගැටලුවක් ඇතැයි මම නොසිතමි , එය එසේ කරන්නේ එය භාවිතා කළ පළමු වතාවට පමණි (පසුව කරන ලද ආයාචනා සමඟ හැඹිලි ප්‍රති result ලයක් ලබා දීම). වැදගත්ම දෙය නම් ගැටුම් වළක්වා ගැනීම සඳහා යමෙකු විශාල දුරක් යා යුතු බව නොව “පද්ධතිමය” isions ට්ටන වලින් වැළකී සිටීමයි. වර්ගයකට intක්ෂේත්‍ර දෙකක් තිබේ නම් oldXසහ newXඒවා එකින් එක වෙනස් වේ නම්, oldX^newX1, 2, 4, හෝ 8 වැනි හැෂ් අගයන්ගෙන් 90% ක් පැවරෙනු ඇත. oldX+newX[පරීක්ෂා නොකළ අංක ගණිතය] භාවිතා කිරීමෙන් වැඩි
isions ට්ටන ඇති

1
... වඩා නවීන ක්‍රියාකාරිත්වයට වඩා, නමුත් විවිධ හැෂ් අගයන් 500,000 ක් ඇති දේවල් 1,000,000 ක එකතුවක් එක් එක් හැෂ් අගයට සම්බන්ධිත කරුණු දෙකක් තිබේ නම් ඉතා හොඳ වනු ඇත. එක් හැෂ් අගයකට දේවල් 500,001 ක් සහ අනෙක් ඒවා එක බැගින් තිබේ නම් ඉතා නරක ය.
supercat

23

මෑතක් වන තුරුම මගේ පිළිතුර ජෝන් ස්කීට්ගේ කතාවට ඉතා සමීප වනු ඇත. කෙසේ වෙතත්, මම මෑතකදී හෑෂ් වගු දෙකක බලය භාවිතා කළ ව්‍යාපෘතියක් ආරම්භ කළෙමි, එනම් අභ්‍යන්තර වගුවේ ප්‍රමාණය 8, 16, 32 වැනි හැෂ් වගු ය. ප්‍රයිම් අංක ප්‍රමාණවලට අනුග්‍රහය දැක්වීමට හොඳ හේතුවක් තිබේ, නමුත් එහි ප්‍රමාණ දෙකේ බලයට ඇති වාසි කිහිපයක් ද වේ.

ඒක ගොඩක් උරා ගත්තා. ඒ නිසා ටිකක් අත්හදා බැලීම් හා පර්යේෂණ වලින් පසු මම පහත දැක්වෙන දෑ සමඟ මගේ හැෂ් නැවත සේදීමට පටන් ගතිමි:

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()විශේෂිත ආකාරයකින් දුප්පත් නම් මිස එය ක්‍රියා නොකළ යුතුය .

හැෂ් කේතයක් නැවත මිශ්‍ර කිරීමෙන් විශාල හැෂ් කේතයක් වැඩි දියුණු කළ නොහැක, මන්ද එකම බලපෑම වන්නේ අපි තවත් isions ට්ටන කිහිපයක් හඳුන්වා දීමයි.

හැෂ් කේතයක් නැවත මිශ්‍ර කිරීමෙන් භයානක හැෂ් කේතයක් වැඩිදියුණු කළ නොහැක, මන්ද එකම ප්‍රති effect ලය වන්නේ උදා. 53 වන අගය මත ගැටීම් විශාල සංඛ්‍යාවක් 18,3487,291 වටිනාකමට වෙනස් කිරීමයි.

හැෂ් කේතය නැවත මිශ්ර පමණක් එහි පරාසය (2 පුරා නිරපේක්ෂ ගැටීම් අවම වශයෙන් තරමක් හොඳින් කළ හැෂ් කේතය වැඩි දියුණු කළ හැකි 32 හැකි අගයන්) නමුත් දරුණු ලෙස හැෂ් වගුවේ සැබෑ භාවිතය සඳහා වසා modulo'd විට ගැටීම් දී. බල-දෙකේ වගුවක සරල මොඩියුලය මෙය වඩාත් පැහැදිලිව පෙනෙන අතර, එය වඩාත් පොදු ප්‍රාථමික සංඛ්‍යා වගු සමඟ negative ණාත්මක බලපෑමක් ඇති කරයි, එය එතරම් පැහැදිලිව පෙනෙන්නට නොතිබුණි (නැවත සකස් කිරීමේ අමතර කාර්යය ප්‍රතිලාභයට වඩා වැඩි වනු ඇත , නමුත් ප්‍රතිලාභ තවමත් පවතී).

සංස්කරණය කරන්න: මම විවෘත ලිපින භාවිතා කරමින් සිටියෙමි, එමඟින් ision ට්ටනයට සංවේදීතාවද වැඩි වනු ඇත, සමහර විට එය බලය දෙකේ බලයට වඩා වැඩි ය.

.නෙට් හි (හෝ මෙහි අධ්‍යයනය ) string.GetHashCode()ක්‍රියාත්මක කිරීම මේ ආකාරයෙන් වැඩිදියුණු කළ හැකිද යන්න කනස්සල්ලට කරුණකි ( අඩු ගැටුම් හේතුවෙන් පරීක්ෂණ 20-30 ගුණයක් වේගයෙන් ධාවනය වන අනුපිළිවෙල අනුව) සහ මගේම හැෂ් කේත කොතරම්ද? වැඩිදියුණු කළ හැකිය (ඊට වඩා බොහෝ දේ).

මම අතීතයේ කේත කරන ලද සහ ඇත්ත වශයෙන්ම මෙම වෙබ් අඩවියේ පිළිතුරු වල පදනම ලෙස භාවිතා කරන ලද සියලු GetHashCode () ක්‍රියාත්මක කිරීම් මා සිතුවාට වඩා නරක ය . බොහෝ විට එය බොහෝ භාවිතයන් සඳහා "ප්‍රමාණවත්" විය, නමුත් මට වඩා හොඳ යමක් අවශ්‍ය විය.

ඒ නිසා මම එම ව්‍යාපෘතිය එක් පැත්තකට දැමුවෙමි (එය කෙසේ හෝ සුරතල් ව්‍යාපෘතියක් විය) .NET හි හොඳ, හොඳින් බෙදා හරින ලද හැෂ් කේතයක් ඉක්මනින් නිෂ්පාදනය කරන්නේ කෙසේදැයි සොයා බැලීමට පටන් ගතිමි.

අවසානයේදී මම ස්පූකි හැෂ් .NET වෙත ගෙන ඒමට කටයුතු කළෙමි . ඇත්ත වශයෙන්ම ඉහත කේතය 32-බිට් ආදානයකින් බිට් 32 ප්‍රතිදානයක් නිපදවීමට ස්පූකි හැෂ් භාවිතා කිරීමේ වේගවත් අනුවාදයකි.

දැන්, ස්පූකි හැෂ් කේත කෑල්ලක් මතක තබා ගැනීමට ඉක්මන් නොවේ. මගේ වරාය ඊටත් වඩා අඩු නිසා වඩා හොඳ වේගය සඳහා මම එය බොහෝමයක් අතින් ලියා ඇත. නමුත් කේත නැවත භාවිතා කිරීම යනු එයයි.

ඉන්පසුව මම එම ව්‍යාපෘතිය එක් පැත්තකට තැබුවෙමි , මන්ද මුල් ව්‍යාපෘතිය මඟින් වඩා හොඳ හැෂ් කේතයක් නිෂ්පාදනය කරන්නේ කෙසේද යන ප්‍රශ්නය නිපදවා ඇති හෙයින් එම ව්‍යාපෘතිය වඩා හොඳ .නෙට් මතක සටහනක් නිෂ්පාදනය කරන්නේ කෙසේද යන ප්‍රශ්නය ඇති කළේය.

පසුව මම නැවත පැමිණ, ස්වදේශීය වර්ග සියල්ලම (except හැර decimal) පහසුවෙන් හැෂ් කේතයකට පෝෂණය කිරීම සඳහා අධික බරක් නිෂ්පාදනය කළෙමි .

එය වේගවත් ය, බොබ් ජෙන්කින්ස් බොහෝ ගෞරවය ලැබීමට සුදුසු වන්නේ මා විසින් ගෙන එන ලද ඔහුගේ මුල් කේතය තවමත් වේගවත් බැවින්, විශේෂයෙන් ඇල්ගොරිතම for සඳහා ප්‍රශස්තිකරණය කර ඇති බිට් 64 යන්ත්‍රවල ය.

සම්පූර්ණ කේතය 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();
}

එය බීජ අගයන් ද ගනී, එබැවින් ඔබට විශ්වාස කළ නොහැකි ආදානය සමඟ කටයුතු කිරීමට අවශ්‍ය නම් සහ හැෂ් ඩොස් ප්‍රහාර වලින් ආරක්ෂා වීමට අවශ්‍ය නම් ඔබට අතිකාල හෝ ඒ හා සමාන බීජයක් සැකසිය හැකි අතර ප්‍රහාරකයන් විසින් ප්‍රති results ල අනපේක්ෂිත කළ හැකිය:

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)වැඩිදියුණු කළ දේවල් ආපසු ලබා දෙන භ්‍රමණ ක්‍රමයක් අතින් ඇලවීමයි . විහිළුකාරයා මා වෙනුවෙන් එය යොමු කරනු ඇතැයි මට විශ්වාසයි, නමුත් පැතිකඩ වෙනත් ආකාරයකින් පෙන්නුම් කරයි.

N decimal.NET දෘෂ්ටිකෝණයෙන් C # වලින් ආවත් එය ස්වදේශීය නොවේ. එහි ඇති ගැටළුව නම්, තමන් විසින්ම GetHashCode()නිරවද්‍යතාව සැලකිය යුතු ලෙස සලකන අතර එය තමන්ගේම Equals()නොවේ. දෙකම වලංගු තේරීම් වේ, නමුත් ඒ හා සමාන නොවේ. ඔබේම අනුවාදය ක්‍රියාත්මක කිරීමේදී, ඔබ එකක් හෝ වෙනත් දෙයක් කිරීමට තෝරා ගත යුතුය, නමුත් ඔබට අවශ්‍ය කුමක්දැයි මට දැනගත නොහැක.

. සංසන්දනය කිරීමෙන්. නූලක් මත භාවිතා කරන්නේ නම්, බිට් 64 හි ඇති ස්පූකි හැෂ් බිටු string.GetHashCode()32 ට වඩා සැලකිය යුතු වේගයකින් යුක්ත වන අතර එය string.GetHashCode()බිට් 64 ට වඩා තරමක් වේගවත් වන අතර එය බිටු 32 හි ස්පූකි හැෂ් වලට වඩා සැලකිය යුතු වේගයකින් යුක්ත වුවද සාධාරණ තේරීමක් කිරීමට තරම් වේගවත් වුවද.


බහු හැෂ් අගයන් එකකට සංයෝජනය කරන විට, මම longඅතරමැදි ප්‍රති results ල සඳහා අගයන් භාවිතා කිරීමට නැඹුරු වන අතර පසුව අවසාන ප්‍රති result ලය a දක්වා අඩු කරන්න int. එය හොඳ අදහසක් සේ පෙනේද? මගේ කනස්සල්ල නම් යමෙක් උදා: hash = (hash * 31) + nextField භාවිතා කරයි, එවිට ගැලපෙන අගයන් යුගල බලපාන්නේ හැෂ් හි ඉහළ බිටු 27 ට පමණි. ගණනය කිරීම a දක්වා විහිදීමට ඉඩ දීම longසහ දේවල් එතීම එම අනතුර අවම කරයි.
සුපර් කැට්

up සුපර්කැට් එය රඳා පවතින්නේ ඔබේ අවසාන ඇඹරීම බෙදා හැරීම මත ය. ස්පූකිලිෂාර්ප් පුස්තකාලය මඟින් බෙදාහැරීම හොඳ බව සහතික කරනු ඇත (එයට වස්තු නිර්මාණය අවශ්‍ය නොවන නිසා) දර්ශකයක් අවිනිශ්චිත වර්ගයකට යැවීමෙන් හෝ එය සෘජුවම හසුරුවන ගණනය කිරීම් වලින් එකක් සම්මත කිරීමෙන්, නමුත් ඔබට දැනටමත් නොපැහැදිලි නම් දත්ත හෝ සුදුසු ගණනය කිරීමක්, ඉන්පසු .Update()ඉහත පිළිතුරට අනුව බහු අගයන් සමඟ ඇමතීම උපක්‍රමය කරයි.
ජෝන් හැනා

On ජෝන්හානා ඔබ මුහුණ දුන් ගැටළු සහගත හැසිරීම පිළිබඳව වඩාත් නිවැරදිව පැවසීමට ඔබ කැමතිද? වටිනාකමින් යුත් වස්තු ක්‍රියාත්මක කිරීම සුළුපටු ( ValueUtils ) බවට පත් කරන පුස්තකාලයක් ක්‍රියාත්මක කිරීමට මම උත්සාහ කරමි. තවද, හැෂ් ටේබල් දෙකේ බලයේ දුර්වල හැෂ් වැරදි බව පෙන්වන ටෙස්ට්සෙට් එකකට මම කැමතියි.
ඊමොන් නර්බෝන්

AmEamonNerbonne “සමස්ත කාලය ඒ ආකාරයෙන් මන්දගාමී විය” යන්නට වඩා නිවැරදි කිසිවක් මා සතුව නොමැත. මම සංස්කරණයක එකතු කළ පරිදි, මම විවෘත-ලිපින භාවිතා කිරීමේ කාරණය බලයේ දෙකේ සාධකයට වඩා වැදගත් වන්නට ඇත. මම වෙනස් ප්‍රවේශයන් කිහිපයක් සංසන්දනය කරන විශේෂිත ව්‍යාපෘතියක පරීක්ෂණ අවස්ථා කිහිපයක් කිරීමට මම සැලසුම් කරමි, එබැවින් ඉහළ ප්‍රමුඛතාවයක් නොතිබුණද (ඊට පසු කිසිදු අවශ්‍යතාවයක් නොමැති පුද්ගලික ව්‍යාපෘතියක්) මට පසුව ඔබට වඩා හොඳ පිළිතුරක් ලැබෙනු ඇත. , ඒ නිසා මම එය වෙත ළඟා වූ විට මම එය වෙතට යන්නෙමි ...)
ජෝන් හන්නා

On ජෝන්හානා: ඔව්, මම දන්නවා පුද්ගලික ව්‍යාපෘති කාලසටහන යන්නේ කෙසේදැයි - වාසනාව! කෙසේ වෙතත්, මම අවසාන ප්‍රකාශය හොඳින් වාක්‍ය ඛණ්ඩනය නොකළ බව මට පෙනේ: මා අදහස් කළේ ගැටළු සහගත ආදානය ඉල්ලා සිටීම මිස එහි ප්‍රති .ලයක් ලෙස ඇති වූ ගැටළු පිළිබඳ විස්තර නොවේ. මම එය පරීක්ෂණ කට්ටලයක් ලෙස භාවිතා කිරීමට කැමතියි (හෝ පරීක්ෂණ කට්ටලයක් සඳහා ආශ්වාදයක්). ඕනෑම අවස්ථාවක - ඔබේ සුරතල් ව්යාපෘතියට වාසනාව :-).
ඊමොන් නර්බෝන්

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 () කිසිදු පරාමිතියක් නොගනී, එබැවින් එය යතුරු දෙකකින් ඇමතීමට අවශ්‍ය වන අතර එය කෙසේ හෝ තීරණය කළ යුතුය. කණගාටුයි, වැඩිදුර පැහැදිලි කිරීමකින් තොරව මෙය දක්ෂ ලෙස පෙනේ, නමුත් එය එතරම් හොඳ නැත.
මයිකල් ස්ටම්

ඔබට සාමාන්‍ය බර පැටවීම අවශ්‍ය වන්නේ ඇයි? සෑම වස්තුවකටම GetHashCode()ක්‍රමයක් ඇති බැවින් වර්ගය වැදගත් නොවේ (සහ ඔබේ කේතයේ භාවිතා නොවේ) , එබැවින් ඔබට සෑම විටම paramsඅරාව පරාමිතිය සමඟ ක්‍රමය භාවිතා කළ හැකිය . නැත්නම් මට මෙහි යමක් මග හැරී තිබේද?
ගෙහෝ

4
ඔබ සාමාන්‍ය විද්‍යාව වෙනුවට වස්තුවක් භාවිතා කරන විට ඔබට GetHashCode හි අවශ්‍ය නොවන බොක්සිං සහ මතක වෙන් කිරීම් ලැබෙනු ඇත. එබැවින් ජනක විද්‍යාව යනු යා යුතු මාර්ගයයි.
CodesInChaos

1
පසුපස මාරුව / xor පියවර ( h += (h << 10); h ^= (h >> 6); h += (h << 3); h ^= (h >> 11); h += (h << 15);කේත කේතයක් ඇත: ඒවා කිසිදු ආදානයක් මත රඳා නොපවතින අතර මට අතිරික්තයක් පෙනේ.
sehe

1
Ag මැග්නස් ඔව් හරි, මම මගේ මුල් අදහස මකා දමමි. මෙහි වෙනත් විසඳුම් තරම් වේගවත් නොවිය හැකි බව කුඩා සටහනක්, නමුත් ඔබ පවසන පරිදි එය වැදගත් නොවේ. බෙදා හැරීම විශිෂ්ටයි, මෙහි ඇති බොහෝ විසඳුම් වලට වඩා හොඳයි, එබැවින් මගෙන් +1! :)
nawfal

11

වන විට https://github.com/dotnet/coreclr/pull/14863 , සුපිරි සරල බව එහි පූරක කේත ජනනය කිරීමේ නව ක්රමයක් තියෙනවා! ලියන්න

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

ක්‍රියාත්මක කිරීමේ විස්තර ගැන කරදර නොවී මෙය ගුණාත්මක හැෂ් කේතයක් ජනනය කරනු ඇත.


එය මිහිරි එකතු කිරීමක් සේ පෙනේ ... නැව්ගත කරන .NET Core හි කුමන අනුවාදය දැන ගැනීමට ක්‍රමයක් තිබේද?
ඩෑන් ජේ

1
AnDanJ මොනතරම් සතුටුදායක අහඹු සිදුවීමක්ද, HashCodeඔබේ අදහස් දැක්වීමට පැය කිහිපයකට පෙර කෝර්ෆ්එක්ස් හි වෙනස්කම් ඒකාබද්ධ කරන ලදි :) වර්ගය .NET Core 2.1 හි නැව්ගත කිරීමට නියමිතය.
ජේම්ස් කෝ

එය නියමයි - සහ ආපසු හැරවීමේ කාලය. ඉහළට. :)
ඩෑන් ජේ

An ඩැන්ජේ ඊටත් වඩා හොඳ ප්‍රවෘත්තියක් - එය දැන් ඩොට්නෙට්-කෝර් මයිජෙට් සංග්‍රහයේ සත්කාරකත්වය සපයන කෝර් එෆ්එක්ස් හි රාත්‍රී ගොඩනැගීම් වලදී ලබා ගත යුතුය.
ජේම්ස් කෝ

මිහිරි - ඉතා නැහැ අපි ඉන්නේ සිට, වැඩ මට උදව් නොවන බව බව ලේ ගැලීම නවීන, එහෙත් හොඳ කියා දැනගන්න. චියර්ස්!
ඩෑන් ජේ

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

මෙන්න මගේ සරල ප්‍රවේශය. මම මේ සඳහා සම්භාව්‍ය සාදන්නා රටාව භාවිතා කරමි. එය ටයිප් සේෆ් (බොක්සිං / අන්බොක්සිං නැත) සහ .නෙට් 2.0 සමඟ අනුකූල වේ (දිගු කිරීමේ ක්‍රම ආදිය නොමැත).

එය මේ ආකාරයට භාවිතා වේ:

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)එක් එක් වර ඇමතීමට වඩා ) ක්‍රම භාවිතා කළ හැකිය .
nawfal

this.result * Prime2 * item.GetHashCode()බොහෝ විට භාවිතා කරන විට ඔබ කරන්නේ this.result * Prime2 + item.GetHashCode()කුමක්ද?
nawfal

ආදිය AddItems<T>(params T[] items)නිසා මට බොහෝ විට භාවිතා කළ නොහැකtypeof(T1) != typeof(T2)
bitbonk

ඔව් මට එය මඟ හැරුණි.
nawfal

5

ReSharper භාවිතා කරන්නන්ට GetHashCode, Equals, සහ වෙනත් අය සමඟ ජනනය කළ හැකිය 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යනු ව්‍යුහයක් වන අතර එය ශක්තිමත් ලෙස GetHashCodeක්‍රියාත්මක වන බව පෙනේ .

ඒ කියන්නේ අපට මෙය සරලව කළ හැකිය:

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

ගේ සඳහා .NET Core වත්මන් ක්රියාත්මක කිරීම දෙසට අවධානය යොමු කරමු ValueTuple's 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;
        }
    }

ඉංග්රීසි භාෂාවෙන්:

  • වම් භ්‍රමණය (රවුම් මාරුව) h1 ස්ථාන 5 කින්.
  • ප්රති result ලය සහ h1 එකට එකතු කරන්න.
  • XOR ප්‍රති result ලය h2 සමඟ.
  • මෙහෙයුම {ස්ථිතික සසම්භාවී බීජ, h1 on මත සිදු කිරීමෙන් ආරම්භ කරන්න.
  • තවත් එක් එක් අයිතමය සඳහා, පෙර ප්‍රති result ලය සහ ඊළඟ අයිතමය මත ක්‍රියාකාරිත්වය සිදු කරන්න (උදා: h2).

මෙම ROL-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();
}

ඒ කියන්නේ ඔබට පුද්ගල හා ගිණුම් යන වස්තු තිබේ නම් සහ ඔවුන් දෙදෙනාම සහ ID = 1 නම්, ඔවුන්ට එකම හැෂ් කේතය ඇත. ඒක හරි නැහැ.
පෙරෝ

15
ඇත්ත වශයෙන්ම ඉහත අදහස වැරදිය. හැෂ්-කේත isions ට්ටනය වීමේ හැකියාව සෑම විටම පවතී (හැෂ් කේතයක් සොයා ගන්නේ බාල්දිය මිස තනි වස්තුව නොවේ). එබැවින් එවැනි ක්‍රියාවට නැංවීම - මිශ්‍ර වස්තූන් අඩංගු හැෂ් කේතයක් සඳහා - ගැටීම් රාශියකට තුඩු දෙනු ඇත, එය නුසුදුසු ය, නමුත් ඔබේ හැෂ් ටේබල්වල තනි වර්ගයක වස්තූන් පමණක් ඔබ සතුව ඇත්නම් එය හොඳ ය. එසේම එය ඒකාකාරව බෙදා හරිනු නොලැබේ, කෙසේ වෙතත් system.object හි මූලික ක්‍රියාත්මක කිරීමද සිදු නොවේ, එබැවින් මම ඒ ගැන ඕනෑවට වඩා කරදර
නොවෙමි

2
හැෂ් කේතය හැඳුනුම්පත විය හැකි බැවින් හැඳුනුම්පත පූර්ණ සංඛ්‍යාවක් වේ. GetHashCode පූර්ණ සංඛ්‍යාවක් මත ඇමතීමට අවශ්‍ය නැත (එය අනන්‍යතා ශ්‍රිතයකි)
ඩැරල් ලී

2
Ar ඩැරල්ලී නමුත් ටොමෝ ඔහුගේ _id මාර්ගෝපදේශකයෙකු විය හැකිය. _id.GetHashCodeඅභිප්‍රාය පැහැදිලිව පෙනෙන පරිදි කිරීම හොඳ කේතීකරණ ක්‍රියාවලියකි .
nawfal

2
24 1224 භාවිත රටාවන් මත පදනම්ව ඔබ දෙන හේතුව නිසා එය භයානක විය හැකි නමුත් එය ද විශිෂ්ට විය හැකිය; ඔබට සිදුරු නොමැති එවැනි සංඛ්‍යා අනුක්‍රමයක් තිබේ නම්, ඔබ පරිපූර්ණ ඇල්මරියක් නිපදවා ඇති අතර, ඕනෑම ඇල්ගොරිතමයකට වඩා හොඳය. එය එසේ බව ඔබ දන්නේ නම්, ඔබට එය ගණන් බලා සමානාත්මතා පරීක්‍ෂණය මඟ හැරිය හැක.
ජෝන් හැනා

3

ඔබට අවශ්‍ය නම් ප්‍රාථමිකයන් ඉහළ නැංවීම පහසු වනවා හැර නයිට්කෝඩර් විසඳුමට බෙහෙවින් සමාන ය.

PS: මෙය පෙරනිමියෙන් 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
ශුන්‍ය හැසිරවිය නොහැක.
JJS

1

ඉහත පිළිතුර ලෙස තෝරාගත් ක්‍රියාත්මක කිරීම භාවිතා කරමින් මම පාවෙන සහ දශම සමඟ ගැටළුවක් ඇති කර ගතිමි.

මෙම පරීක්ෂණය අසමත් වේ (පාවෙන; මම අගයන් 2 ක් negative ණ ලෙස මාරු කළද හැෂ් එක සමාන වේ):

        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));

නමුත් මෙම පරීක්ෂණය සමත් වේ (අඟල් සමඟ):

        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));

ප්‍රාථමික වර්ග සඳහා GetHashCode භාවිතා නොකිරීමට මම මගේ ක්‍රියාත්මක කිරීම වෙනස් කළ අතර එය වඩා හොඳින් ක්‍රියා කරන බව පෙනේ

    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() විට int අගයන් xor'ed වන අතර ප්‍රති result ලය ඔබේ හැෂ් වේ.

හැෂ් හැඳුනුම්පත ලෙස භාවිතා කරන අයට (මම අදහස් කළේ අද්විතීය අගයක්), හැෂ් ස්වාභාවිකවම ඉලක්කම් ගණනාවකට සීමා වී ඇති අතර, මම සිතන්නේ එය ඇල්ගොරිතම සඳහා අවම වශයෙන් MD5 වත් බයිට් 5 ක් බවයි.

ඔබට බහු අගයන් හැෂ් කළ අගයකට හැරවිය හැකි අතර සමහර ඒවා සමාන වේ, එබැවින් එය හඳුනාගැනීමක් ලෙස භාවිතා නොකරන්න. (සමහර විට යම් දවසක මම ඔබේ සංරචකය භාවිතා කිරීමට යන්නේ)


7
හැෂ් කේතයක් සෑදීම සඳහා එක්ස්පෝරින් සංඛ්‍යා යනු සුප‍්‍රසිද්ධ ප‍්‍රති-රටාවකි, එමඟින් තථ්‍ය-ලෝක වටිනාකම් සමඟ විශේෂයෙන් ඉහළ ගැටුම් සංඛ්‍යාවක් ඇති වේ.
ජෝන් හැනා

මෙහි සිටින සෑම කෙනෙක්ම පූර්ණ සංඛ්‍යාවක් භාවිතා කරන අතර, හැෂ් එක හා සමාන වීමට කිසිඳු ආකාරයක සහතිකයක් නොතිබුණි, එය සිදුවීමට ඇත්තේ ගැටීම් කිහිපයක් ඇති තරමට වෙනස් වීමටය.
deadManN

ඔව්, නමුත් ඔබේ දෙවන හා පස්වන ගැටුම් වළක්වා ගැනීමට උත්සාහ නොකරන්න.
ජෝන් හැනා

1
ඔව්, එම ප්‍රති-රටාව සාමාන්‍ය දෙයක්.
ජෝන් හන්නා

2
ළඟා වීමට ශේෂයක් තිබේ. ස්පූකිහාෂ් වැනි හොඳ හැෂ් කේතයක් භාවිතා කරන්න, එවිට ඔබට වඩා හොඳ, ision ට්ටන වැළැක්වීම ලැබෙනු ඇත, නමුත් මේ සියල්ලටම වඩා ගණනය කිරීමේ කාලයක් එයට ඇත (නමුත් ඉතා විශාල දත්ත ප්‍රමාණයක් හෑෂ් කිරීමේදී, ස්පූකිහාෂ් අතිශයින්ම ඉක්මන් වේ). Xoring කිරීමට පෙර එක් අගයක සරල මාරුවක් යනු ision ට්ටනය අඩු කිරීම සඳහා ආන්තික අමතර පිරිවැයක් පමණි. ප්‍රයිම්-අංක ගුණ කිරීම නැවත කාලය හා ගුණාත්මකභාවය යන දෙකම වැඩි කරයි. මාරුව හෝ බහු අතර වඩා හොඳ කුමන නිසා විවාදාත්මක ය. සරල xor බොහෝ විට සත්‍ය දත්ත මත ගැටීම් රාශියක් ඇති අතර එය වළක්වා ගත හැකිය
ජෝන් හන්නා

1

මෙය ජොෂ් බ්ලොච් ක්‍රියාත්මක කිරීම ක්‍රියාත්මක කරන ස්ථිතික උපකාරක පන්තියකි; බොක්සිං ක්‍රීඩාව "වැළැක්වීම" සඳහා පැහැදිලි බර පැටවීම සහ දිගු ප්‍රාථමිකයන් සඳහා විශේෂයෙන් හැෂ් ක්‍රියාත්මක කිරීම.

ඔබේ ක්‍රියාවට නැංවීමට සමාන වන නූල් සංසන්දනයක් ඔබට සමත් කළ හැකිය.

හැෂ් ප්‍රතිදානය සැමවිටම int එකක් බැවින් ඔබට හැෂ් ඇමතුම් දාමයක් කළ හැකිය.

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, එය බොක්සිං හේතුවෙන් මතකය වෙන් කරනු ඇත

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.