If I do the overloading of operator ==
for a class, I must perform some checks before comparing the fields:
if both arguments are null, or both arguments are the same instance, then returns true
Example: if (System.Object.ReferenceEquals(arg1, arg2)) return true;
if one is null, but not both, then returns false
Example: if (((object)arg1 == null) || ((object)arg2 == null)) return false;
Indeed, if I have a struct and I want to do the overloading of operator ==
, these checks are not necessary, rather they are useless, for the following reasons: a struct is a value type, so it can not be null, for example DateTime date = null;
is not valid, because DateTime
(that is a struct) is not a reference type, so you can not compare two DateTime
, one of which is set to null
.
I created a simple struct Point2D
with operator ==
, then I compare an instance of Point2D
with null
:
Point2D point = new Point2D(0,0);
Console.WriteLine((point == null));
Obviously the operator ==
it is not called, but the comparison returns False
. Which method is called?
The documentation states that overloading this operator in not-immutable types is not recommended. Why?
Chris Shain's answer is correct but does not explain why this is legal.
When you override the equality operator, and both the operands are non-nullable value types, and the the return type is bool, then for free we give you a lifted operator. That is, if you have
public static bool operator ==(S s1, S s2) { ... }
then for no additional cost you get
public static bool operator ==(S? s1, S? s2) { ... }
It is that operator that is being called. Of course the compiler knows that the result will always be false because one of the operands is null and the other never is.
There used to be a warning to the effect that your code always returns false but we accidentally disabled it a couple versions back and never actually turned it back on. I'm working on this very code tomorrow in the Roslyn compiler so I'll see what I can do to get it back in shape.
Because it seems that the compiler optimizes this away. I tried this code:
System.Drawing.Point point = new System.Drawing.Point(0,0);
Console.WriteLine((point == null));
And it generated the following IL:
IL_0000: ldloca.s 00
IL_0002: ldc.i4.0
IL_0003: ldc.i4.0
IL_0004: call System.Drawing.Point..ctor
IL_0009: ldc.i4.0
IL_000A: call System.Console.WriteLine
This ultimately boils down to "Create a Point, and then write false to the command line"
This also explains why it doesn't call your operator. A struct can never be null, and in circumstances where the compiler can guarantee that you are always going to get false as a result, it doesn't bother issuing code to call an operator at all.
The same thing happens with this code, even though String is a class and overload the == operator:
System.Drawing.Point point = new System.Drawing.Point(0,0);
Console.WriteLine("foo" == null);
As for immutability... The == operator in C# is generally interpreted to mean "reference equality", e.g. these two variables point to the same instance of the class. If you are overloading it, then you generally mean to say that two instances of the class, while not the same instance, should behave as if they were the same instance when their data is the same. The classic example is Strings. "A" == GiveMeAnA()
even though the actual String reference returned by GiveMeAnA
may not be the same as the one represented by the literal "A"
.
If you overloaded the ==
operator on classes which were not immutable, then mutation of a class after ==
had been evaluated could cause numerous subtle bugs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With