Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing boxed values of different types

Tags:

c#

boxing

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!

like image 486
Logerfo Avatar asked Aug 29 '18 13:08

Logerfo


2 Answers

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!

like image 125
Igor Yalovoy Avatar answered Sep 20 '22 05:09

Igor Yalovoy


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;
    }
}
like image 40
AndreyCh Avatar answered Sep 23 '22 05:09

AndreyCh