Skip to main content

How to Resolve Warning "CS0659: 'MyClass' overrides Object.Equals but does not override Object.GetHashCode" in C#

The Compiler Warning CS0659 is a code correctness warning. The message reads: " 'MyClass' overrides Object.Equals(object o) but does not override Object.GetHashCode()".

In C# and .NET, the Equals method and the GetHashCode method are intrinsically linked. If you override Equals to define custom logic for when two objects are considered "equal" (e.g., based on ID rather than memory reference), you must also override GetHashCode. If you don't, hash-based collections (like Dictionary and HashSet) will fail to work correctly with your objects, leading to bugs where items cannot be found or duplicates are added.

This guide explains the contract between these two methods and how to implement them correctly.

Understanding the Relationship

The contract for .NET objects states: "If two objects are equal according to .Equals(), they must return the same value from .GetHashCode()."

  • Equals(a, b): Determines if two objects represent the same data.
  • GetHashCode(): Generates a numeric "bucket" ID for the object used by Dictionaries and HashSets.

If you override Equals (to say "User 1 matches User 1") but leave the default GetHashCode, the default implementation usually relies on the memory address of the object. Since two different objects have different memory addresses, they generate different hash codes. The Dictionary looks in the wrong bucket and claims the item doesn't exist, even though Equals says it does.

Scenario: Breaking Hash-Based Collections

This warning signals that your code will behave unexpectedly in collections. Even though Equals is correct, the lack of GetHashCode breaks lookups.

Example of warning:

using System;
using System.Collections.Generic;

public class Product
{
public int Id { get; set; }
public string Name { get; set; }

// We override Equals to compare based on ID
public override bool Equals(object obj)
{
if (obj is Product other)
{
return this.Id == other.Id;
}
return false;
}

// ⛔️ Warning CS0659: We forgot to override GetHashCode!
// The compiler knows this will cause trouble.
}

public class Program
{
static void Main()
{
var p1 = new Product { Id = 100, Name = "Laptop" };
var p2 = new Product { Id = 100, Name = "Laptop" };

// This works fine:
Console.WriteLine($"Are they equal? {p1.Equals(p2)}"); // True

// This FAILS because of the missing GetHashCode:
var inventory = new HashSet<Product>();
inventory.Add(p1);

// HashSet uses GetHashCode first. It sees p2 has a different hash than p1.
// It concludes p2 is NOT in the list.
bool contains = inventory.Contains(p2);
Console.WriteLine($"Is p2 in set? {contains}"); // False (Unexpected!)
}
}

Solution 1: Using HashCode.Combine (Modern .NET)

If you are using .NET Core 2.1, .NET 5+, or .NET Standard 2.1, the easiest and safest way to implement this is using the HashCode struct. It handles nulls and algorithms automatically.

You should combine the exact same properties that you used in your Equals method.

Solution:

using System;

public class Product
{
public int Id { get; set; }
public string Name { get; set; }

public override bool Equals(object obj)
{
return obj is Product other && this.Id == other.Id;
}

// ✅ Correct: Generate a hash based on the same property used in Equals
public override int GetHashCode()
{
return HashCode.Combine(Id);
}
}
tip

If your Equals logic used both Id and Name, your hash code should act accordingly: return HashCode.Combine(Id, Name);.

Solution 2: Legacy XOR Implementation

If you are working on older .NET Framework versions that do not have HashCode.Combine, the standard practice is to multiply primes or use XOR (^) operations, while being careful with null checks.

Solution:

public class Product
{
public int Id { get; set; }
public string Name { get; set; }

public override bool Equals(object obj)
{
return obj is Product other && this.Id == other.Id && this.Name == other.Name;
}

// ✅ Correct: Manual implementation
public override int GetHashCode()
{
unchecked // Allow overflow
{
int hash = 17;
hash = hash * 23 + Id.GetHashCode();
// Handle null string safely
hash = hash * 23 + (Name != null ? Name.GetHashCode() : 0);
return hash;
}
}
}

Conclusion

CS0659 is a warning that prevents subtle bugs in data collections.

  1. The Rule: If you override Equals, you must override GetHashCode.
  2. The Consistency: The properties used to calculate the Hash Code must be the same properties used to determine Equality.
  3. The Implementation: Use HashCode.Combine(prop1, prop2) in modern .NET for a robust implementation.