基于堆栈的值类型的比较

13

我在这里试图实现的是对装箱基本类型进行直接值比较。

((object)12).Equals((object)12); // Type match will result in a value comparison,
((object)12).Equals((object)12d); // but a type mismatch will not. (false)
object.Equals((object)12,(object)12d); // Same here. (false)
我理解了“为什么”,但我不知道“如何”实现。
这些类型在运行时是未知的,可以是数据源中的任何原始类型,包括字符串、日期时间、布尔值等等。 我已经采取了一个丑陋的方法,编写了一个扩展方法,在进行“==”比较之前计算出两个类型并进行转换:(为了完整起见,我包括了每种原始类型以及我感兴趣的类型)
public static bool ValueEquals(this object thisObj, object compare)
    {
        if (thisObj is int)
        {
            int obj = (int)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            if (compare is uint)
                return (obj == (uint)compare);
            if (compare is decimal)
                return (obj == (decimal)compare);
            if (compare is float)
                return (obj == (float)compare);
            <... and so on for each primitive type ...>
        }
        if (thisObj is uint)
        {
            uint obj = (uint)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            if (compare is uint)
                return (obj == (uint)compare);
            <... Again for each primitive type ...>
        }
        if (thisObj is decimal)
        {
            decimal obj = (decimal)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            <... Etc, etc ...>

最终的方法达到了300多行,虽然有点丑陋但还可以接受。但现在我需要比较不仅仅是“==”,我还需要">"、"<"、"<="、">="和"!="。

在反射中有没有什么可以用于装箱值类型比较的东西?

有任何东西吗?


所以你只想比较两个可能包含相同值的不同类型的盒装值?你不想使用Equals(),因为大多数不同类型的比较都会返回false。我建议尝试类似以下的方法... - James Michael Hare
使用C# 4.0的dynamic关键字代替。 - Hans Passant
我会避免使用动态类型,因为这样更容易出现运行时问题。使用下面的解决方案,你至少可以在编译时知道两种类型是可转换的,并避免许多运行时错误。 - James Michael Hare
动态关键字不会比较不同类型的动态类型。==操作符会抛出运行时异常。 - johnDisplayClass
虽然建议的答案没有解决我的问题,但我已经成功地使用提供的两个答案找到了一个解决方案。IConvertible是关键。对于相等性,我现在使用Convert.ChangeType在两个参数上返回一个字符串,然后简单地使用==。对于>、<、>=、<=等,我返回一个十进制数并使用适当的运算符。太棒了! - johnDisplayClass
嘿,我添加了一个重载,希望对你有更好的作用,请在答案中查看。我知道它已经被标记为已回答(谢谢),但希望以一种非常通用的方式实现它。 - James Michael Hare
2个回答

8

看起来你假设arg1的类型是你想要转换的类型,因此我会使用这样的通用方法。只要arg2是可转换的(int、double、所有数字类型和字符串等都可以转换),这个方法就可以正常工作:

public static bool ValueEquality<T1, T2>(T1 val1, T2 val2) 
    where T1 : IConvertible 
    where T2 : IConvertible
{
    // convert val2 to type of val1.
    T1 boxed2 = (T1) Convert.ChangeType(val2, typeof (T1));

    // compare now that same type.
    return val1.Equals(boxed2);
}

** 更新 ** 使两种类型都成为通用参数,可以推断出两种类型,并增加了对 arg2 的编译时安全性检查,以确保它在编译时是可转换的。

给定这个通用函数,现在所有以下情况都返回 true(不需要指定类型参数,因为从第一个参数中推断出来):

        Console.WriteLine(ValueEquality(1, "1"));
        Console.WriteLine(ValueEquality(2, 2.0));
        Console.WriteLine(ValueEquality(3, 3L));

更新

根据您的评论,如果您只有对象,则可以使用以下重载。两者可以共存,并且它将根据参数调用更合适的一个:

    public static bool ValueEquality(object val1, object val2)
    {
        if (!(val1 is IConvertible)) throw new ArgumentException("val1 must be IConvertible type");
        if (!(val2 is IConvertible)) throw new ArgumentException("val2 must be IConvertible type");

        // convert val2 to type of val1.
        var converted2 = Convert.ChangeType(val2, val1.GetType());

        // compare now that same type.
        return val1.Equals(converted2);
    }

这也适用于对象:

        object obj1 = 1;
        object obj2 = 1.0;

        Console.WriteLine(ValueEquality(obj1, obj2));

正如我所说,这两个可以共存作为重载,因此如果直接比较兼容的IConvertible类型,它将使用通用的方法,如果只有装箱类型作为对象,则会使用对象重载。

很遗憾,我只有对象可用作参数(装箱值类型)。这对我没有用。 - johnDisplayClass
更新了一个重载。你可以使用两个版本,编译器将根据显式的IConvertible参数或对象选择正确的版本。 - James Michael Hare
注意:假设 val1 是一个 int 类型的变量,val2 是一个 long 类型的变量,且其值大于 int.MaxValue 。这个方法会抛出 OverflowException 异常,而不是返回 false,这在语义上并不是我期望的结果。 - cdhowie

3

考虑使用IComparable而非手动if语句 - http://msdn.microsoft.com/en-us/library/system.icomparable.compareto.aspx

如果您将来需要类似的内容,请首先根据一个操作数的类型进行开关,然后为每个类型实现“操作处理程序”类,并为每个类型编写处理操作的方法,例如IntOpHandler.PerformOp(int left, object right)

您还可以通过将多个类型合并在一起来减少需要处理的类型数量(即:byte、short、ushort、int、uint、long - 首先转换为long,然后对long执行操作)。


这必须是一个答案。问题是关于使用<,>,<=等符号的。而IComparable接口正在给出答案。 - IgorStack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接