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!
dynamic
as the data type instead of in a cast? dynamic
only states that the field's type can change at runtime... –
Steric o1
and o2
are given and can't be changed –
Haig Object
at all? Why don't you leave it as the real type? If you are concerned about performance you should not box everything in the first place. –
Haig 2L
and the new call uses 2m
, the cache is currently not used, although I want it to be used. –
Kalat o1.GetType()
and o2.GetType()
. If it does not exist, return false
(meaning not equal). –
Kalat dynamic
will fail. It's basically when there are ambiguous or no ==
operators exist. For example, consider object o1 = 3UL
and object o2 = 3L
. –
Jeroldjeroma 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;
}
}
I think using dynamic is best approach to this, other solution may be something like this (Marshall is for casting always to bigger type)
private static bool compareObj(object obj1, object obj2)
{
bool flag = true;
try
{
object result = Convert.ChangeType(obj1, obj2.GetType());
object result2 = Convert.ChangeType(obj2, obj1.GetType());
var first = Marshal.SizeOf(obj1.GetType());
var second = Marshal.SizeOf(obj2.GetType());
if (first > second)
{
flag = obj1.Equals(result2);
}
else
{
flag = obj2.Equals(result);
}
}
catch (InvalidCastException ex)
{
flag = false;
}
return flag;
}
© 2022 - 2024 — McMap. All rights reserved.
object == object
checks for reference-equality. So when you box your struct it´ll be compared to another instance of that struct via reference-equality which is never true. – Carolinianobject.Equals(object)
meet your needs? – Amnesia((object)2m).Equals((object)2L)
returnsfalse
as well. – Kalatdecimal
you can usebool equal = Convert.ToDecimal(obj1) == Convert.ToDecimal(obj2);
– Haig==
operator (it would be a compilation error if tried), the method should returnfalse
. – KalatObject.Equals
might work here – Vincentia