C# 当装箱类型未知时,检查不同类型的装箱值类型的相等性

C# 当装箱类型未知时,检查不同类型的装箱值类型的相等性,c#,equality,value-type,boxing,C#,Equality,Value Type,Boxing,我将int与运行时未知的装箱数字类型进行比较。此代码对具有相同值的不同类型的未绑定值执行成功的值比较: short UnboxedShort = short.MaxValue; object BoxedShort = short.MaxValue; //this boxed type is unknown at runtime int UnboxedInt = short.MaxValue; Console.WriteLine(UnboxedInt == UnboxedShort); //re

我将int与运行时未知的装箱数字类型进行比较。此代码对具有相同值的不同类型的未绑定值执行成功的值比较:

short UnboxedShort = short.MaxValue;
object BoxedShort = short.MaxValue; //this boxed type is unknown at runtime
int UnboxedInt = short.MaxValue;

Console.WriteLine(UnboxedInt == UnboxedShort); //returns true
Console.WriteLine(UnboxedInt.Equals(UnboxedShort)); //returns true
此代码无法编译,因为我正在对值类型和对象使用相等运算符:

Console.WriteLine(UnboxedInt == BoxedShort); //doesn't compile
因此,如果我这样做,它似乎会起作用,因为我正在调用底层的
.Equals()
方法:

Console.WriteLine(UnboxedInt.Equals(BoxedShort)); //returns false
它返回false。一开始,它似乎在进行引用检查,因为short被装箱在一个对象中。但是,当我将未装箱的短码与装箱的短码进行比较时,它返回
true

Console.WriteLine(object.Equals(BoxedShort, UnboxedShort)); //returns true
如果类型未知,则唯一可以使比较生效的方法是使用
Convert.ChangeType()

不过,这不是首选方法,因为根据我过去运行的测试,
Convert.ChangeType()
速度很慢。由于我将在迭代中进行此比较,因此我希望避免
Convert.ChangeType()

那么,在框架中到底发生了什么呢?在这个框架中,有框短和无框短相等,有框短和无框int不相等,但无框短和无框int相等

更新

IvanStoev在评论中建议我尝试
Convert.ToInt32()
。如果已知类型(
(int)(short)BoxedShort
),则比取消装箱慢3.47倍,这显然不是一个选项,但仍然比
Convert.ChangeType()快4.17倍


我会等一会儿,看看是否还有其他答案。

一切都有合理的解释

首先,当比较不同类型的“收件箱”(即类型化)值和“未装箱”值时,编译器会考虑定义的
隐式转换,并最终加宽其中一个值,然后使用相应的
=
运算符

当您有一个装箱值并使用
Equals
方法时,情况就不同了,因为
Equals
实现通常只检查相同的类型,而不检查性能。以下是取自以下内容的
Int32
的实现:

其他实现类似。我想现在你明白为什么boxed
int
不等于boxed
short
具有相同的值,反之亦然

因为没有简单的方法可以在不知道类型的情况下取消绑定值,所以您应该真正使用转换。所有基本类型都实现了
IConvertible
,允许将装箱的值转换为所需的类型(如果可能)

但是,您不应该像这样使用Convert.ChangeType

bool test = UnboxedInt == Convert.ToInt32(BoxedShort);

这之所以更快,是因为
Convert.ChangeType
首先需要使用类型检查并实际调用一个具体的
ToXXX
方法。其次,由于
Convert.ChangeType
的返回类型是
object
,因此该值被装箱。最后,您需要将其解装箱回
int
(现在知道它会成功)。换句话说,与直接的
ToXXX
方法调用相比,额外的检查、分支、box和unbox基本上调用了
IConvertible.ToXXX
实现。

为什么不
unbxedint==Convert.ToInt32(…。它应该是快的,基本上委托给代码> iCOVALITY。ToTut32)/代码> @ IvtoStov AH,它甚至没有发生在我身上。让我来测试这个快速的性能。@ IvtoStov我更新了我的答案。你应该考虑添加你的评论作为答案,如果没有人想出一个更有效的方法,我会把它标记出来。
Explicit unbox (175.8 ms)
Convert.ToInt32 (611.6 ms)
Convert.ChangeType (2555 ms)

At 50,000,000 iterations, Convert.ToInt32 is...
   435.80 ms (347.90%) slower than Explicit unbox
    Lose 1 second over 114,731,528 iterations
  1943.40 ms (417.76%) faster than Convert.ChangeType
    Gain 1 second over 25,728,105 iterations
public override bool Equals(Object obj) {
    if (!(obj is Int32)) {
        return false;
    }
    return m_value == ((Int32)obj).m_value;
}
bool test = UnboxedInt == Convert.ToInt32(BoxedShort);