C#
GetHashCode
coding guidelines
.NET
programming best practices

GetHashCode Guidelines in C

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

GetHashCode matters whenever an object is used in Dictionary<TKey, TValue>, HashSet<T>, or any other hash-based collection. The method does not need to produce a unique number for every object, but it must follow strict consistency rules with equality or the collection will behave incorrectly.

The First Rule Is Consistency with Equals

If two objects are equal according to Equals, they must return the same hash code. This is not an optimization guideline; it is a correctness requirement.

csharp
1public sealed class Person
2{
3    public string FirstName { get; }
4    public string LastName { get; }
5
6    public Person(string firstName, string lastName)
7    {
8        FirstName = firstName;
9        LastName = lastName;
10    }
11
12    public override bool Equals(object? obj)
13    {
14        return obj is Person other &&
15               FirstName == other.FirstName &&
16               LastName == other.LastName;
17    }
18
19    public override int GetHashCode()
20    {
21        return HashCode.Combine(FirstName, LastName);
22    }
23}

If Equals compares FirstName and LastName, then GetHashCode must use the same identity-defining fields. Using only one of them would increase collisions and could break lookup behavior when equality says two objects are different.

Equal Objects Must Stay Stable While in a Hash Collection

A hash code can change only if the equality-defining state changes. In practice, that means objects used as dictionary keys should usually be immutable.

csharp
1var person = new Person("Ada", "Lovelace");
2var set = new HashSet<Person> { person };
3
4Console.WriteLine(set.Contains(person));

If Person were mutable and one of its key fields changed after insertion, the object could end up in the wrong bucket. Then Contains or Remove might fail even though the object is still physically present in the set.

Do Not Treat Hash Codes as Permanent IDs

A hash code is not a stable external identifier. Different runs of an application can produce different hash codes for the same logical value, especially for framework types such as string. Hash codes are for in-memory hashing, not for persistence, database keys, or cross-process protocols.

That means you should never serialize a hash code and expect it to remain meaningful later.

Prefer HashCode.Combine in Modern C#

Older examples often use manual multiplication with prime numbers. That still works, but modern .NET provides HashCode.Combine, which is clearer and less error-prone.

csharp
1public override int GetHashCode()
2{
3    return HashCode.Combine(Id, Name, CreatedOn);
4}

If your type has many fields, combining the fields that actually participate in equality is usually enough. Do not include every property by reflex. Include only the ones that define object identity.

Value Objects and Reference Identity Are Different

Not every class should override GetHashCode. If reference identity is the intended behavior, the base implementation may be correct. Override GetHashCode when the type has value semantics and you are also overriding Equals.

csharp
1public sealed class Money : IEquatable<Money>
2{
3    public decimal Amount { get; }
4    public string Currency { get; }
5
6    public Money(decimal amount, string currency)
7    {
8        Amount = amount;
9        Currency = currency;
10    }
11
12    public bool Equals(Money? other)
13    {
14        return other is not null &&
15               Amount == other.Amount &&
16               Currency == other.Currency;
17    }
18
19    public override bool Equals(object? obj) => Equals(obj as Money);
20
21    public override int GetHashCode() => HashCode.Combine(Amount, Currency);
22}

This is a classic value object. Two Money instances with the same amount and currency should behave as equal values in dictionaries and sets.

Collisions Are Allowed, but Bad Distribution Hurts Performance

Different objects may share the same hash code. That is normal. The goal is not uniqueness; it is a reasonably even distribution. Poor distribution leads to more collisions and slower lookups because more equality comparisons are needed within the same bucket.

What you should avoid is simplistic implementations such as returning a constant or hashing only the first field of a multi-field key.

Common Pitfalls

  • Overriding Equals without overriding GetHashCode to match it.
  • Using mutable fields in the hash code for objects that will live in dictionaries or sets.
  • Treating hash codes as persistent identifiers or data that should survive across program runs.
  • Including fields in GetHashCode that are not part of logical equality, or omitting fields that are.
  • Returning overly simplistic values that cause unnecessary collisions.

Summary

  • 'GetHashCode must agree with Equals for all equal objects.'
  • Types used as hash keys should usually be immutable with respect to equality-defining fields.
  • Hash codes are for in-memory hashing, not for storage or external identity.
  • 'HashCode.Combine is the preferred modern implementation style in C#.'
  • Good hash codes do not need to be unique, but they should distribute values reasonably well.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.