Skip to content
Free public beta until December 4, 2026: every course is open, no card needed. After that, the first chapter of each course stays free and full access is a one-time $39 ($19 for up to 100 founding members chosen for helpful feedback). Your progress is kept. How the beta works

Every way to compare two values

==, Equals, records, strings, IEquatable, IComparable and comparers. What "the same" means for each kind of type, and how to change it.

First ask what kind of type you have, because that decides what == does before you've written a line.

  • Numbers, bool, char, enums → == compares the values. For double, compare within a tolerance, not with ==.
  • string → == compares the characters, case and all. For user input and keys, say which rule you mean: string.Equals(a, b, StringComparison.OrdinalIgnoreCase).
  • A record → == compares every property. That's usually what you want for data.
  • A class → == asks "is this the same object?". Two separate Customer objects with the same email are not equal unless the class overrides Equals and GetHashCode (or implements IEquatable<T>).
  • You need an order, not just equal or not (sort products by price) → IComparable<T> on the type, or a key with LINQ's OrderBy when the order depends on the situation.
  • A dictionary or set should treat keys differently (ignore case in usernames) → pass a comparer when you create it: new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase).

If you override Equals, override GetHashCode too, or dictionaries and HashSet<T> will lose your objects.

The rule, by kind of type

Formal. For classes, == and the default Equals compare references (the same object) unless the class overloads them. string overloads == to compare contents ordinally. Records (class and struct) get compiler-generated value equality: equal when every member is equal, comparing reference-type members by their own equality. A plain struct has a field-by-field Equals but no == operator unless it declares one.

In plain English. For most objects, "equal" means "the very same object", not "the same data". Strings and records are the exceptions you use most: two strings with the same letters are equal, and two records holding the same values are equal.

Example. new Point(1, 2) == new Point(1, 2) is true for record Point(int X, int Y) and false for an ordinary class Point.

Why this rule exists. Identity matters for things like accounts and windows; value matters for data like coordinates and money. C# makes you pick by picking the kind of type.

Value types and records compare contents; classes compare references unless they say otherwise.

What every type inherits from object, and what to override to change "equal".

The compiler writes Equals, GetHashCode and == for you, from the properties.

Text

Ordinal or culture-aware, with or without case, and the rule of thumb for choosing.

A comparer passed to the dictionary's constructor changes what counts as the same key.

Equality and order you define

A typed Equals that HashSet and Dictionary can use, and a natural sort order for Sort and OrderBy.

Sort by a key you choose at the call, with tie-breakers, instead of building the order into the type.

The set operators decide "same item" by the type's equality, or by a comparer you pass.