-1

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.

Rabadash8820
  • 2,328
  • 3
  • 27
  • 49
  • How about `string` class? – Ivan Stoev Sep 24 '16 at 22:09
  • Wow, that's a great example, I feel like an idiot... Can you give me any other examples? If so, I might just take this question down. – Rabadash8820 Sep 24 '16 at 22:12
  • Hmm, that's the most famous. Let see - another that I can think of are anonymous types and tuples. – Ivan Stoev Sep 24 '16 at 22:16
  • 1
    Tuples...also a good one. Well what do you think, between the referenced SO post and this other one I just found (http://stackoverflow.com/questions/104158/what-is-best-practice-for-comparing-two-instances-of-a-reference-type?rq=1), is it worth keeping this question up? – Rabadash8820 Sep 24 '16 at 22:22
  • It's up to you. I think it's primarily opinion-based. I find using operator `==` and `!=` more "natural" then `Equals` method, so overriding both for classes that have "value semantics" does make sense to me. After all, you can always use `ReferenceEquals` in the rare cases when you need a "real" reference equality. – Ivan Stoev Sep 24 '16 at 22:33
  • 1
    Btw, [Eric Lippert](http://stackoverflow.com/users/88656/eric-lippert) has some posts about equality which you may find interesting - [C#: Inconsistent equality](http://blog.coverity.com/2014/01/13/inconsistent-equality/#.V-cCFY9OJZU) and [Sharp Regrets: Top 10 Worst C# Features #9: Too much equality](http://www.informit.com/articles/article.aspx?p=2425867) – Ivan Stoev Sep 24 '16 at 22:49
  • 1
    Awesome, thanks for the links. I really appreciate the guidance! – Rabadash8820 Sep 24 '16 at 23:12
  • Fundamentally, your question is answered in the marked duplicate, as well as questions such as https://stackoverflow.com/questions/17573720/should-an-override-of-equals-on-a-reference-type-always-mean-value-equality and https://stackoverflow.com/questions/34766369/do-i-need-to-override-equal. That said, your question is also based on an incorrect and illogical reading of the documentation. MSDN is _not_ saying that immutable types should be value types. Only that value types should be immutable. It also is _not_ saying that _"you only want reference semantics"_ for reference types. – Peter Duniho Sep 25 '16 at 03:14

1 Answers1

1

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.

Emperor Eto
  • 2,456
  • 2
  • 18
  • 32
  • "Many controls (like ListBox's) will actually create shallow copies" -- that hasn't been my experience; can you provide code to reproduce that behavior? – 15ee8f99-57ff-4f92-890c-b56153 Sep 25 '16 at 02:46
  • For your second paragraph, `coll.Where(x => x.ID == someItemID)` serves very well. – 15ee8f99-57ff-4f92-890c-b56153 Sep 25 '16 at 02:49
  • Ed - Now that you mention it I'm having a hard time reproducing the problem that I previously encountered, but there was a scenario where I needed to override Equals in order to make ListBox.ScrollIntoView, SelectedItem, and related members work properly. I recall the explanation given to me for why was that there was a situation where a shallow copy was being made. I'll edit my answer to clarify. – Emperor Eto Sep 25 '16 at 14:06
  • I've had the same problem, but it wasn't the ListBox creating the copies, it was deserialization. – 15ee8f99-57ff-4f92-890c-b56153 Sep 25 '16 at 14:08
  • Yeah that'll for sure do it. As will different database queries. Or re-reading items from disk. Lots of scenarios where it can occur. – Emperor Eto Sep 25 '16 at 14:18
  • +1 for mentioning database queries, that's a great example of different instances with the same data (though I would think that most db libraries do some kind of caching that actually would use the same instances, at least until ur result sets get too big). However, when comparing result sets, ur definition of equality tends to change (e.g. compare by name one time, compare by address another). So it seems like Linq or custom Comparers would work fine without any need to override Equals () – Rabadash8820 Sep 25 '16 at 15:03