35

.NET Standard 2.1 / .NET Core 3 introduce System.HashCode to quickly combine fields and values to a hash code without having to care about the underlying implementation.

However, it only provides Combine method overloads for up to 8 values. What do I do if I have a class with 9 values (3x3 matrix) or even 16 values (4x4 matrix)?

Should I simply add together the results of two Combine calls, passing as many values as possible in each?

public override int GetHashCode() => HashCode.Combine(M11, M12, M13, M21, M22, M23, M31, M32) + HashCode.Combine(M33); 

Looking at the source, I cannot completely argue if this may have implications I don't know of.

2 Answers 2

68

As stated in the System.HashCode documentation, adding together hashes returned by successive HashCode.Combine calls is NOT the solution.

While the static HashCode.Combine method overloads only allow up to 8 values, these are just convenience methods - to combine more, instantiate the HashCode class itself and use it as follows:

public override int GetHashCode() { HashCode hash = new(); hash.Add(M11); hash.Add(M12); hash.Add(M13); hash.Add(M21); hash.Add(M22); hash.Add(M23); hash.Add(M31); hash.Add(M32); hash.Add(M33); return hash.ToHashCode(); } 

It does make me wonder why there is no HashCode constructor accepting a params object[] values so you could do all that in one line, but there are probably reasons I didn't think of this quickly. (S. the comments why such an overload does not exist.)

Sign up to request clarification or add additional context in comments.

6 Comments

It might have something to do with allocating an array, which can be a performance drag. Since .NET Core is more performance oriented, I would think that that's the reason.
Not only array allocation but also boxing of every value type. Horribly inefficient.
It neither does boxing nor array allocation
@Vlad Why not? Surely it has to do with performance considerations..
@Vlad I think the concerns about boxing and array allocation were made about a constructor accepting a params object[] values which absence I mentioned in my answer, not the calls to HashCode.Add.
|
3

If you happen to be able to use C# 13 (.NET 9), and all your types are the same, you can write a simple generic helper method that avoids boxing as well as the array allocation.

This example uses C# 14 in order to make it a static extension method off HashCode, but if you can only use C# 13, you can just make this a static method in a helper class.

extension(HashCode) { public static int Create<T>(params ReadOnlySpan<T> values) { var hashCode = new HashCode(); foreach (var value in values) { hashCode.Add(value); } return hashCode.ToHashCode(); } } 

Usage:

HashCode.Create(M11, M12, M13, M21, M22, M23, M31, M32, M33); 

Behind the scenes, the compiler will make use of InlineArray in order to avoid the allocation.

Of note, if this is absolutely performance critical, it will not beat @Ray's answer for speed. But we're only talking on the order of single-digit ns slower.

1 Comment

Thanks for the update, wish I could accept multiple answers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.