1

I am trying to add an equals function to compare two structs, but i am not sure the correct way to write it, or whether both of these functions are valid.

    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }

        Tile other = (Tile) obj;

        return other == this;
    }

And

    public override bool Equals(object obj)
    {
        if (obj is Tile other)
        {
            return other == this;
        }
        return false;
    }

My == operator is:

    public static bool operator ==(Tile a, Tile b)
    {
        return a.X == b.X && b.Z == a.Z;
    }

Are these functions the same or are they different in subtle ways that i might not be aware of?

WDUK
  • 1,412
  • 1
  • 12
  • 29
  • 1
    The only thing that makes sense here is `return a.X == b.X && b.Z == a.Z;` even then its suspect – TheGeneral Aug 21 '20 at 01:56
  • 2
    You can even shorten it to `return obj is Title other && other == this;` – juharr Aug 21 '20 at 01:57
  • @MichaelRandall what are you confused by with the Equals implementation ? – WDUK Aug 21 '20 at 01:58
  • 1
    These two are not equivalent if `Tile` is an abstract class and your implementing two subclasses. If you change `other == this` to be `other.X == this.X`, the first methodology would return false, and the second methodology would return true. – Hayden Aug 21 '20 at 02:03
  • I think we should take a step back, show your class, explain exactly what you are trying to do, and ask for an example of how to implement a `GetHashCode` and equality check on a custom class. – TheGeneral Aug 21 '20 at 02:04
  • 1
    Well they are structs as mentioned in the question so they can't be abstract. Microsoft says `If you are implementing a value type, you should consider overriding the Equals method to gain increased performance over the default implementation of the Equals method on ValueType.` https://learn.microsoft.com/en-us/visualstudio/ide/reference/generate-equals-structs?view=vs-2019 And the hash code i got from here: https://stackoverflow.com/a/7813738/5736835 – WDUK Aug 21 '20 at 02:06
  • Obviously in real code you will make sure `object.Equals` never get called on the struct so to some extent it does not matter... – Alexei Levenkov Aug 21 '20 at 02:42

1 Answers1

0

Without a minimal, complete, and verifiable code example, it's impossible to have complete confidence in whether the code examples you've posted may or may not be equivalent. However, assuming there is no other operator overloads besides the one you posted, they seem to be equivalent.

They both ultimately will compare the two objects in question using the operator == overload provided. So the only possible difference would be in respect to the type-checking done. And both effectively will return false with the obj value is null or of the wrong type.

It seems to me that this could easily have been tested by a simple program. And as noted in the comments for your question, ideally one would avoid calling the Equals() method for a value type anyway (i.e. to avoid boxing/unboxing overhead), rendering the specific implementation moot. So it's not really clear what the purpose of asking the question is. If you have some scenario where there is some unexpected behavior using one or the other of these implementations, you should post a new question, but provide a good MCVE that reproduces the issue that you are dealing with, along with a detailed explanation of what specifically you need help with.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • `one would avoid calling the Equals() method for a value type anyway` But microsoft mentions i should for performance reasons which i commented above with link to the article in question. Also structs can't be null as this is for struct comparison. I did test and they both work but doesn't mean there isn't a "gotcha" waiting to rear its ugly head in ways i am not aware of. When creating the hash for my `Tile` struct which just has a x,z position i saw people implement `Equals()` with the `GetHeadCode()` function so i was basically following suit...since i presumed it was required. – WDUK Aug 21 '20 at 07:04
  • _"microsoft mentions i should for performance reasons"_ -- yes, they do. But 1) that advice was formulated before .NET had generics, so boxed value types were a lot more common, and 2) it's reasonable to be conservative, i.e. backstop potentially less-than-optimal code by at least implementing a value type efficiently, if possible/reasonable. – Peter Duniho Aug 21 '20 at 09:01
  • _"structs can't be null as this is for struct comparison"_ -- structs can't be null, but you can certainly pass a null value to the `System.Object.Equals(object)` method. That a struct itself can't be null is completely irrelevant. You still need to be able to handle a null argument value. – Peter Duniho Aug 21 '20 at 09:02
  • _"When creating the hash for my Tile struct which just has a x,z position i saw people implement Equals() with the GetHeadCode() function so i was basically following suit...since i presumed it was required"_ -- do you mean `GetHashCode()`? Why would you implement `GetHashCode()` _without_ overriding `Equals()`? Those two methods go hand in hand, but if you're only going to override one, it's the `Equals()` method that would be critical to implement, not `GetHashCode()`. – Peter Duniho Aug 21 '20 at 09:04
  • I am overriding it it's in the original question, I was explaining why I had the function in the first place for my structs. – WDUK Aug 21 '20 at 11:08
  • Nonresponsive. That doesn't answer the question I asked. You wrote that you only override `Equals()` _because_ you override `GetHashCode()`. That you did is immaterial (and clear from the question). The question is, how did you get yourself into a situation where you thought that overriding `GetHashCode()` was the first thing you did, only to determined later that _because_ you did so, now you need to override `Equals()`? In any case, I answer the _question_. I don't mind addressing an additional follow-up comment or two, but if you have in-depth questions, you need to post a new question. – Peter Duniho Aug 21 '20 at 16:27
  • I did it because whenever i saw people's code with GetHashCode() it always had an override of Equals(). Even microsoft has it in their code example: https://learn.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=netcore-3.1 `Derived classes that override GetHashCode() must also override Equals(Object) to guarantee that two objects considered equal have the same hash code; otherwise, the Hashtable type might not work correctly.` I need to use a hash table for for my structs so I implemented it. – WDUK Aug 21 '20 at 19:54
  • _"I did it because whenever i saw people's code with GetHashCode() it always had an override of Equals()"_ -- again, not the question I asked. I already know why you override `Equals()`. The question was why the `GetHashCode()` in the first place. Why _start_ there? Which brings me to: _"I need to use a hash table for for my structs"_ Hash tables only are useful if you have a mechanism for recognizing values already in the table. They are an _optimization_ over a simpler equality-relational collection. Why would you use a hash table for objects that don't already implement some value equality? – Peter Duniho Aug 21 '20 at 20:03
  • I'm using a hash table because my tiles are hexagonal not rectangular. Hexagons don't make for efficient storage even in axial coordinate systems. But a hash table means i don't waste memory. You can read about that here: https://www.redblobgames.com/grids/hexagons/#map-storage – WDUK Aug 21 '20 at 20:10
  • _"a hash table means i don't waste memory"_ -- well, since an array would work just as well, actually it does mean you waste memory. You can tile hexagons in 2-d array and a tiny amount of math. And there are actually many other options, all of which don't involve hash codes at all. See https://www.redblobgames.com/grids/hexagons/ for pretty much everything you ever might have wanted to know about hexagonal tiles. Furthermore, if a hash table actually _did_ help, it wouldn't work without a value-equality implementation. So again, my question has not been answered. – Peter Duniho Aug 21 '20 at 20:14
  • That said, none of this really matters that much. You're mistaken about the original problem domain, but I've already answered the question you had. If you have other concerns, you need to post those in a new question. – Peter Duniho Aug 21 '20 at 20:15