Search code examples
c#.netclassstructequality

Why Use Value Equality On a Reference Type


This MSDN article spells out when to declare a type as a value type (struct) vs a reference type (class). In particular, it says:

AVOID defining a struct unless the type has all of the following characteristics:

  • It logically represents a single value, similar to primitive types (int, double, etc.).
  • It has an instance size under 16 bytes.
  • It is immutable.
  • It will not have to be boxed frequently.

In all other cases, you should define your types as classes.

Then, this MSDN article spells out some guidelines for overriding Object.Equals() on a type, including:

When a type is immutable, ... overloading operator == to compare value equality instead of reference equality can be useful because, as immutable objects, they can be considered the same as long as they have the same value. It is not a good idea to override operator == in non-immutable types.

So it seems to me, if a type is meant to be immutable, you would just declare it a struct and override ValueType.Equals(). But, if your type is declared a class, you only want reference semantics, in theory. This SO post seems to support that. So my question is, why would you ever bother overriding Equals() on a reference type? Doing so is like saying that reference semantics aren't good enough for your reference type, which makes no sense to me. The only example I can think of is if you have a large (>16 byte instances) class that has a mix of mutable and immutable fields, but even then it seems like reference semantics would be all you need.


Solution

  • To your specific question - "why would you ever bother overriding Equals() on a reference type?": You would override Equals() if you want two instances of the class to be treated as equal by other .NET methods even if they are not the same instance.

    Consider List<T>.Contains(). By overriding Equals() you can ensure that method gives you the correct result even if the specific class instances being compared are not in fact the same instance.

    I myself encountered this a lot in WPF. I had a situation where certain controls (ListBox in particular) had copies of my objects that were not the same instances as what I was testing them against. This made methods like ScrollIntoView, and the SelectedItem property, not work correctly until I overrode Equals on the class. I can think of one particular example where this would for sure happen: suppose you had a Person class with a list of instances that were created as a result of a database query. You fill a ListBox with that result set. Then later on, you have a new result set from a different DB query (a filter of some kind, perhaps), and now want to select all the items in the ListBox that are in the second query result. If you don't override Equals, then you won't just be able to set SelectedItems, because the instances in your ListBox won't be the same as the instances in that second query. As is pointed out in the comments, there are other ways around this, but in my opinion overriding Equals offers the cleanest solution.

    That's just one example. I'm sure you will encounter others before long.