operator overloading
equality operator
null comparison
programming
C#

Overriding operator. How to compare to null?

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

When overloading == in C#, comparing to null is one of the easiest places to create accidental recursion or inconsistent behavior. The safe pattern is to use ReferenceEquals to check whether an operand is actually null at the reference level, then perform your value comparison only after that. If you overload ==, you should also overload !=, override Equals, and keep GetHashCode consistent with your equality logic.

Why null Is Tricky

If you implement == and then write if (left == null) inside that implementation, you may call your own overloaded operator again. That creates infinite recursion.

Bad idea:

csharp
// if (left == null) { ... }

Inside the operator, use ReferenceEquals(left, null) instead. That bypasses the overloaded equality logic and performs a raw reference check.

Safe Equality Operator Pattern

csharp
1using System;
2
3public sealed class Person
4{
5    public string Name { get; }
6
7    public Person(string name)
8    {
9        Name = name;
10    }
11
12    public static bool operator ==(Person? left, Person? right)
13    {
14        if (ReferenceEquals(left, right))
15            return true;
16
17        if (ReferenceEquals(left, null) || ReferenceEquals(right, null))
18            return false;
19
20        return left.Name == right.Name;
21    }
22
23    public static bool operator !=(Person? left, Person? right) => !(left == right);
24
25    public override bool Equals(object? obj) => obj is Person other && this == other;
26
27    public override int GetHashCode() => Name.GetHashCode();
28}

This pattern handles:

  • both operands null
  • exactly one operand null
  • non-null value comparison

without calling itself recursively.

Why ReferenceEquals(left, right) Comes First

If both references point to the same object, they are equal without any further work. That also correctly handles the case where both are null.

csharp
if (ReferenceEquals(left, right))
    return true;

That line is both fast and semantically correct.

Keep Equals and GetHashCode Aligned

Overloading == alone is not enough. If two objects compare equal with ==, they should also compare equal with Equals, and they should usually produce the same hash code.

That matters for:

  • dictionaries
  • hash sets
  • LINQ distinct operations
  • general consistency across the type

If equality semantics differ between == and Equals, the type becomes hard to reason about.

Example Usage

csharp
1var a = new Person("Ava");
2var b = new Person("Ava");
3Person? c = null;
4
5Console.WriteLine(a == b);   // True
6Console.WriteLine(a == c);   // False
7Console.WriteLine(c == null); // True

This is the kind of behavior most developers expect when a type is implementing value-based equality.

When Not to Overload ==

Do not overload == unless the type genuinely has value semantics that people will expect to compare directly.

For example, domain objects identified by database identity or mutable entities with complicated lifecycle rules may be poor candidates. Overloading equality makes the API more powerful, but it also raises the bar for correctness.

If you are not prepared to define consistent equality rules, leaving the default reference equality may be safer.

Nullability Annotations Help

In modern C#, nullable reference annotations make the operator signature clearer:

csharp
public static bool operator ==(Person? left, Person? right)

This expresses that either operand may legally be null and makes the contract visible to the compiler and readers.

Records and Built-In Equality

If your type is primarily a value carrier, consider whether a record is a better fit. Records already implement value-based equality and reduce the need for custom operator code.

csharp
public record Person(string Name);

That does not answer every equality design problem, but it removes a lot of repetitive plumbing for straightforward value objects.

Common Pitfalls

The biggest mistake is using left == null inside the overloaded operator and causing recursive self-calls. Another is implementing == without also implementing !=, Equals, and GetHashCode consistently. Developers also sometimes overload equality for mutable entity types where stable value semantics are unclear. Finally, forgetting nullable cases leads to operators that work for ordinary comparisons but fail on the simplest null checks.

Summary

  • Use ReferenceEquals for null checks inside an overloaded == operator.
  • Check ReferenceEquals(left, right) first to handle same-reference and both-null cases.
  • Overload != alongside ==.
  • Keep Equals and GetHashCode consistent with your equality operator.
  • Consider records if the type is meant to behave like a value object.

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.