Overriding GetHashCode
Keep equality and hashing consistent.
Overriding GetHashCode is a free C# Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why GetHashCode Matters
Hash-based collections like Dictionary and HashSet use GetHashCode to bucket items. If you override Equals but not GetHashCode, these collections can fail to find equal items.
The Golden Rule
The contract is simple: if two objects are equal, they must return the same hash code. The reverse is not required; different objects may share a hash code (a collision).
using System;
public struct Point
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public override bool Equals(object obj) => obj is Point p && p.X == X && p.Y == Y;
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
var a = new Point(1, 2);
var b = new Point(1, 2);
Console.WriteLine(a.Equals(b));
Console.WriteLine(a.GetHashCode() == b.GetHashCode());
}
}Use HashCode.Combine
The HashCode.Combine helper mixes several field values into a well-distributed hash. It is the recommended modern way to implement GetHashCode.
using System;
public struct Color
{
public int R, G, B;
public Color(int r, int g, int b) { R = r; G = g; B = b; }
public override bool Equals(object obj) => obj is Color c && c.R == R && c.G == G && c.B == B;
public override int GetHashCode() => HashCode.Combine(R, G, B);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Color(255, 0, 0).GetHashCode() == new Color(255, 0, 0).GetHashCode());
}
}Base Equality and Hashing Together
Always override Equals and GetHashCode as a pair, using the same fields in both. Using different fields makes equal objects produce different hashes.
using System;
public class Book
{
public string Title;
public int Year;
public Book(string title, int year) { Title = title; Year = year; }
public override bool Equals(object obj)
=> obj is Book b && b.Title == Title && b.Year == Year;
public override int GetHashCode() => HashCode.Combine(Title, Year);
}
public class Program
{
public static void Main()
{
var a = new Book("C#", 2020);
var b = new Book("C#", 2020);
Console.WriteLine(a.GetHashCode() == b.GetHashCode());
}
}Why Dictionaries Need It
A Dictionary first hashes the key to find a bucket, then uses Equals within that bucket. A wrong hash sends a lookup to the wrong bucket and the key is never found.
using System;
using System.Collections.Generic;
public struct Coord
{
public int X, Y;
public Coord(int x, int y) { X = x; Y = y; }
public override bool Equals(object obj) => obj is Coord c && c.X == X && c.Y == Y;
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
var map = new Dictionary<Coord, string> { [new Coord(2, 3)] = "hit" };
Console.WriteLine(map[new Coord(2, 3)]);
}
}Hashing Immutable Fields
Hash codes should be stable for an object while it lives in a hash collection. Base the hash on immutable fields; if a key mutates after insertion, the collection can lose track of it.
using System;
public class Account
{
public readonly int Id; // immutable, safe to hash
public string Nickname; // mutable, do not hash
public Account(int id, string nick) { Id = id; Nickname = nick; }
public override bool Equals(object obj) => obj is Account a && a.Id == Id;
public override int GetHashCode() => Id.GetHashCode();
}
public class Program
{
public static void Main()
{
var acc = new Account(42, "old");
int h1 = acc.GetHashCode();
acc.Nickname = "new"; // does not change the hash
Console.WriteLine(h1 == acc.GetHashCode());
}
}Handling Null Fields
When a field can be null, HashCode.Combine handles it safely. If you compute manually, guard against null to avoid a NullReferenceException.
using System;
public class Tag
{
public string Name;
public Tag(string name) { Name = name; }
public override bool Equals(object obj) => obj is Tag t && t.Name == Name;
public override int GetHashCode() => HashCode.Combine(Name); // null-safe
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Tag(null).GetHashCode() == new Tag(null).GetHashCode());
}
}Collisions Are Normal
Hash codes are 32-bit, so collisions are inevitable for large data sets. A good hash just spreads values to keep collisions rare; Equals resolves any that occur.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var set = new HashSet<int>();
for (int i = 0; i < 5; i++) set.Add(i);
// Equality still works perfectly even though hashing is imperfect in general
Console.WriteLine(set.Contains(3));
Console.WriteLine(set.Contains(99));
}
}Records Generate It For You
A record auto-generates a correct GetHashCode from its properties, matching its value-based Equals. This is the easiest way to get hashing right.
using System;
using System.Collections.Generic;
public record Coord(int X, int Y);
public class Program
{
public static void Main()
{
var set = new HashSet<Coord> { new Coord(1, 1) };
Console.WriteLine(set.Contains(new Coord(1, 1)));
}
}A Correct Equatable Type
Putting it together: same fields in Equals and GetHashCode, immutable keys, and HashCode.Combine for the mix. This type works flawlessly as a dictionary key.
using System;
using System.Collections.Generic;
public struct ProductKey
{
public string Sku;
public int Variant;
public ProductKey(string sku, int variant) { Sku = sku; Variant = variant; }
public override bool Equals(object obj)
=> obj is ProductKey k && k.Sku == Sku && k.Variant == Variant;
public override int GetHashCode() => HashCode.Combine(Sku, Variant);
}
public class Program
{
public static void Main()
{
var stock = new Dictionary<ProductKey, int>
{
[new ProductKey("ABC", 1)] = 10
};
Console.WriteLine(stock[new ProductKey("ABC", 1)]);
}
}Try It Yourself
Prove the contract holds: build two equal keys and confirm both their equality and their hash codes match, then use them in a set.
using System;
using System.Collections.Generic;
public struct Name
{
public string First, Last;
public Name(string first, string last) { First = first; Last = last; }
public override bool Equals(object obj) => obj is Name n && n.First == First && n.Last == Last;
public override int GetHashCode() => HashCode.Combine(First, Last);
}
public class Program
{
public static void Main()
{
var a = new Name("Ada", "Lovelace");
var b = new Name("Ada", "Lovelace");
Console.WriteLine(a.Equals(b) + " " + (a.GetHashCode() == b.GetHashCode()));
var people = new HashSet<Name> { a };
Console.WriteLine(people.Contains(b));
}
}Quick Check
Recall the hashing contract.
Recap
Correct hashing keeps hash-based collections working.
- Equal objects must return the same hash code.
- Use
HashCode.Combineover the same fields asEquals. - Hash immutable fields so keys stay findable.
- Collisions are normal;
Equalsresolves them. - Records generate a correct implementation automatically.
Frequently asked questions
Is the “Overriding GetHashCode” lesson free?
Yes — the full text of “Overriding GetHashCode” is free to read here on the web, and the C# Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO.
What will I learn in “Overriding GetHashCode”?
Keep equality and hashing consistent. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Overriding GetHashCode” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Implementing IEquatable
- Overriding GetHashCode
- Implementing IComparable
- IComparer for Custom Sorting