比较不同类型的装箱值

时间:2018-08-29 13:21:37

标签: c# boxing

在C#中,可以将值类型装箱,这会导致某些比较问题,特别是针对不同类型。示例:invalidate返回2m == 2L,但是true返回(object)2m == (object)2L。我的问题是:在示例情况下,是否可以编写一个获取两个对象(装箱值)参数并返回false的比较方法?它必须适用于任何值类型组合,并且如果值未装箱,则其行为与true运算符相同。谢谢!

3 个答案:

答案 0 :(得分:7)

我建议为该任务使用dynamic

object o1 = 2m;
object o2 = 2L;

if ((dynamic)o1 == (dynamic)o2) { Console.WriteLine("Works like charm"); }

但是,我还没有完全意识到dynamic关键字的所有含义,请注意!

答案 1 :(得分:0)

我认为使用动态方法是解决此问题的最佳方法,其他解决方案也可能是这样(Marshall总是将其强制转换为更大的类型)

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;
}

答案 2 :(得分:0)

由于接受的dynamic解决方案在找不到合适的==运算符时可能会引发异常(请参阅我对比较3UL3L值的评论),我实现了另一种检查相等性的方法。

如果两个装箱的值属于同一类型,则下面的代码将调用Equals方法,或者尝试通过拆箱将其与一个普通类型进行比较。 floatdouble类型经过特殊处理,其余整数类型通过转换为decimal进行比较。

此方法比公认的解决方案慢一点,但从内存分配的角度来看,它处理更多的情况并表现更好:

|          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 |      - |     - |     - |         - |

AreEqualConvert方法的实现:

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;
    }
}