In C#, value types can be boxed, which leads to certain comparison issues, specially for different types. Example: 2m == 2L
returns true
, but (object)2m == (object)2L
returns false
. My question is: is it possible to write a comparison method that gets two objects (boxed values) parameters and returns true
in the example case? It has to work for any value type combination and have the same behavior as the ==
operator if the values were unboxed. Thanks!
I suggest to use dynamic
for that task.
object o1 = 2m;
object o2 = 2L;
if ((dynamic)o1 == (dynamic)o2) { Console.WriteLine("Works like charm"); }
Yet, I am not fully aware of all implications of dynamic
keyword, so take care!
Since the accepted solution with dynamic
may throw an exception when it fails to find a proper ==
operator (see my comment about comparing 3UL
and 3L
values), I've implemented another way to check equality.
The code below invokes Equals
method if the both boxed values are of the same type, or tries to compare the values by unboxing to a common type. The float
and double
types have special processing, the rest integer types are compared by converting to decimal
.
This method is little bit slower than the accepted solution, but it handles more cases and performs better from memory allocation perspective:
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------------- |----------:|---------:|---------:|-------:|------:|------:|----------:|
| AreEqualDynamic | 83.31 ns | 1.447 ns | 1.354 ns | 0.0172 | - | - | 72 B |
| AreEqualConvert | 112.44 ns | 1.156 ns | 0.902 ns | - | - | - | - |
The AreEqualConvert
method implementation:
public bool AreEqualConvert(object o1, object o2)
{
if (ReferenceEquals(o1, o2))
{
return true;
}
if (o1 is null || o2 is null)
{
return false;
}
if (o1.GetType() == o2.GetType())
{
return o1.Equals(o2);
}
switch (o1)
{
case float f1:
switch (o2)
{
case double d2:
return f1 == d2;
case IConvertible c2:
return f1 == c2.ToSingle(null);
default:
return false;
}
case double d1:
return o2 is IConvertible conv2
? d1 == conv2.ToDouble(null)
: false;
case IConvertible c1:
switch (o2)
{
case float f2:
return c1.ToSingle(null) == f2;
case double d2:
return c1.ToDouble(null) == d2;
case IConvertible c2:
return c1.ToDecimal(null) == c2.ToDecimal(null);
default:
return false;
}
default:
return false;
}
}
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