.net - हरण - एक ओवरराइड सिस्टम के लिए सबसे अच्छा एल्गोरिदम क्या है। ऑब्जेक्ट। गेटहाशकोड?




मानक एल्गोरिथ्म (11)

ज्यादातर मामलों में जहां इक्वाल्स () एकाधिक फ़ील्ड की तुलना करता है, इससे कोई फर्क नहीं पड़ता कि आपका GetHash () एक फ़ील्ड या कई पर हैश हो। आपको बस यह सुनिश्चित करना होगा कि हैश की गणना वास्तव में सस्ता है ( कोई आवंटन , कृपया) और तेज़ ( कोई भारी गणना नहीं और निश्चित रूप से कोई डेटाबेस कनेक्शन नहीं) और एक अच्छा वितरण प्रदान करता है।

भारी भारोत्तोलन बराबर () विधि का हिस्सा होना चाहिए; जितना संभव हो उतना सामान पर कॉलिंग इक्वाल्स () को सक्षम करने के लिए हैश को बहुत सस्ता ऑपरेशन होना चाहिए।

और एक अंतिम युक्ति: GetHashCode () पर एकाधिक अनुप्रयोग रनों पर स्थिर होने पर भरोसा न करें । कई .Net प्रकार अपने हैश कोड को पुनरारंभ करने के बाद समान रहने की गारंटी नहीं देते हैं, इसलिए आपको केवल स्मृति डेटा संरचनाओं में GetHashCode () के मान का उपयोग करना चाहिए।

.NET System.Object.GetHashCode विधि में .NET बेस क्लास लाइब्रेरीज़ में, कई स्थानों पर उपयोग किया जाता है। खासकर जब संग्रह में वस्तुओं को तेजी से या समानता निर्धारित करने के लिए। क्या मेरे कस्टम वर्गों के लिए GetHashCode ओवरराइड को कार्यान्वित करने के तरीके पर एक मानक एल्गोरिदम / सर्वोत्तम अभ्यास है, इसलिए मैं प्रदर्शन को कम नहीं करता हूं?


माइक्रोसॉफ्ट पहले से ही एक अच्छा जेनेरिक हैशकोड जनरेटर प्रदान करता है: बस अपनी संपत्ति / फ़ील्ड मानों को एक अनाम प्रकार में कॉपी करें और हैश इसे:

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

यह किसी भी संपत्ति के लिए काम करेगा। यह मुक्केबाजी या अतिरिक्त संसाधनों का उपयोग नहीं करता है। यह सिर्फ पहले से ही अज्ञात प्रकार के ढांचे में लागू एल्गोरिदम का उपयोग करता है।


मैं आमतौर पर जोश ब्लोच के शानदार प्रभावशाली जावा में दिए गए कार्यान्वयन की तरह कुछ जाता हूं। यह तेज़ है और एक बहुत अच्छा हैश बनाता है जो टकराव का कारण बनने की संभावना नहीं है। दो अलग-अलग प्राइम नंबर चुनें, उदाहरण के लिए 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 आईएनएचकोड्स के सामान्य अभ्यास से बेहतर है। मान लें कि हमारे पास दो int फ़ील्ड के साथ एक प्रकार है:

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

वैसे, पहले एल्गोरिदम वर्तमान में अज्ञात प्रकारों के लिए सी # कंपाइलर द्वारा उपयोग किया जाता है।

यह पृष्ठ काफी कुछ विकल्प देता है। मुझे लगता है कि ज्यादातर मामलों के लिए उपरोक्त "पर्याप्त अच्छा" है और यह याद रखना और सही होने के लिए अविश्वसनीय रूप से आसान है। FNV विकल्प समान सरल है, लेकिन संयोजन संयोजन के रूप में ADD बजाय विभिन्न स्थिरांक और XOR का उपयोग करता है। यह नीचे दिए गए कोड की तरह दिखता है , लेकिन सामान्य एफएनवी एल्गोरिदम व्यक्तिगत बाइट्स पर काम करता है, इसलिए 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;
    }
}

ध्यान दें कि एक चीज के बारे में पता होना चाहिए कि आदर्श रूप से आपको अपने समानता-संवेदनशील (और इस प्रकार हैशकोड-संवेदनशील) स्थिति को उस संग्रह में जोड़ने के बाद बदलना चाहिए जो हैश कोड पर निर्भर करता है।

documentation अनुसार:

आप अपरिवर्तनीय संदर्भ प्रकारों के लिए GetHashCode को ओवरराइड कर सकते हैं। आम तौर पर, परिवर्तनीय संदर्भ प्रकारों के लिए, आपको केवल GetHashCode को ओवरराइड करना चाहिए यदि:

  • आप उन क्षेत्रों से हैश कोड की गणना कर सकते हैं जो उत्परिवर्तनीय नहीं हैं; या
  • आप यह सुनिश्चित कर सकते हैं कि एक म्यूटेबल ऑब्जेक्ट का हैश कोड तब नहीं बदलेगा जब ऑब्जेक्ट उस संग्रह में निहित है जो उसके हैश कोड पर निर्भर करता है।

यह एक अच्छा है:

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

यहां मेरा हैशकोड सहायक है।
इसका लाभ यह है कि यह सामान्य प्रकार के तर्कों का उपयोग करता है और इसलिए मुक्केबाजी का कारण नहीं बनता है:

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

यहां मेरी सहायक कक्षा है जो जॉन स्कीट के कार्यान्वयन का उपयोग करती है ।

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 struct HashCode
{
    private readonly int hashCode;

    public HashCode(int hashCode)
    {
        this.hashCode = hashCode;
    }

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

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

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

    public override int GetHashCode()
        => this.hashCode;
}

यह अभी भी सामान्य है, यह अभी भी किसी भी ढेर आवंटन से बचाता है और इसका उपयोग बिल्कुल उसी तरह किया जाता है:

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

मार्टिन की टिप्पणी के बाद अपडेट करें:

obj != null कारण मुक्केबाजी तो मैं डिफ़ॉल्ट तुलनात्मक के लिए स्विच किया।

  • डिफ़ॉल्ट तुलनात्मक प्रदर्शन के संबंध में यह उत्तर देखें।
  • शून्य प्रश्नों के हैश कोड के बारे में चर्चा के लिए यह प्रश्न देखें।

ReSharper users can generate GetHashCode, Equals, and others with 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;
    }
}

As of https://github.com/dotnet/coreclr/pull/14863 , there is a new way to generate hash codes that is super simple! Just write

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

This will generate a quality hash code without you having to worry about the implementation details.


I ran into an issue with floats and decimals using the implementation selected as the answer above.

This test fails (floats; hash is the same even though I switched 2 values to be 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));

But this test passes (with 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));

I changed my implementation to not use GetHashCode for the primitive types and it seems to work better

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

Microsoft lead for several way of hashing...

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

I can guess that for multiple big int you can use this:

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;

And same for multi-type: all converted first to int using GetHashCode() then the int values will be xor'ed and the result is your hash.

For those who use hash as ID (I mean an unique value), hash is naturally limited to a number of digits, I think it was 5 bytes for hashing algorithm, at least MD5.

You may turn multiple values to a hashed value and some of them be same, so don't use it as an identifier. (maybe some day I am going to use your component)


Pretty much similar to nightcoder's solution except it's easier to raise primes if you want to.

PS: This is one of those times where you puke a little in your mouth, knowing that this could be refactored into one method with 9 default's but it would be slower, so you just close your eyes and try to forget about it.

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




gethashcode