A strange piece of code I've just discovered in C# (should also be true for other CLI languages using .NET's structs
).
using System;
public class Program
{
public static void Main(string[] args)
{
int a;
long b;
a = 0;
b = 0;
Console.WriteLine(a.Equals(b)); // False
Console.WriteLine(a.Equals(0L)); // False
Console.WriteLine(a.Equals((long)0)); // False
Console.WriteLine(a.Equals(0)); // True
Console.WriteLine(a.Equals(a)); // True
Console.WriteLine(a == b); // True
Console.WriteLine(a == 0L); // True
Console.WriteLine();
Console.WriteLine(b.Equals(a)); // True
Console.WriteLine(b.Equals(0)); // True
Console.WriteLine(b.Equals((int)0)); // True
Console.WriteLine(b.Equals(b)); // True
Console.WriteLine(b == a); // True
Console.WriteLine(b == 0); // True
}
}
Two interesting points here (assuming that a
is int
and b
is long
):
a != b
, but b == a
;(a.Equals(b)) != (a == b)
Is there any reason why comparison was implemented this way?
Note: .NET 4 was used if it makes any difference.
Introduction to the C# integersIntegers are whole numbers for example -1, 0, 1, 2, 3. C# uses the integral numeric types to represent integer numbers.
Zeros are ignored at the start of an int . If you need the zeros to be displayed, store the number as a String instead. If you need to use it for calculations later, you can convert it to an int using Integer.
In general, Equals()
methods are not supposed to return true for objects of different types.
a.Equals(b)
calls int.Equals(object)
, which can only return true for boxed Int32
s:
public override bool Equals(Object obj) {
if (!(obj is Int32)) {
return false;
}
return m_value == ((Int32)obj).m_value;
}
b.Equals(a)
calls long.Equals(long)
after implicitly converting the int
to a long
.
It therefore compares the two long
s directly, returning true.
To understand more clearly, look at the IL generated by this simpler example (which prints True False True):
int a = 0;
long b = 0L;
Console.WriteLine(a == b);
Console.WriteLine(a.Equals(b));
Console.WriteLine(b.Equals(a));
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldc.i4.0
IL_0003: conv.i8
IL_0004: stloc.1
IL_0005: ldloc.0 //Load a
IL_0006: conv.i8 //Cast to long
IL_0007: ldloc.1 //Load b
IL_0008: ceq //Native long equality check
IL_000A: call System.Console.WriteLine //True
IL_000F: ldloca.s 00 //Load the address of a to call a method on it
IL_0011: ldloc.1 //Load b
IL_0012: box System.Int64 //Box b to an Int64 Reference
IL_0017: call System.Int32.Equals
IL_001C: call System.Console.WriteLine //False
IL_0021: ldloca.s 01 //Load the address of b to call a method on it
IL_0023: ldloc.0 //Load a
IL_0024: conv.i8 //Convert a to Int64
IL_0025: call System.Int64.Equals
IL_002A: call System.Console.WriteLine //True
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