c# - 当装箱类型未知时检查不同类型的装箱值类型是否相等

标签 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); //returns true
Console.WriteLine(UnboxedInt.Equals(UnboxedShort)); //returns true

此代码无法编译,因为我在值类型和对象上使用了相等运算符:

Console.WriteLine(UnboxedInt == BoxedShort); //doesn't compile

所以,如果我这样做,它似乎会起作用,因为我正在调用底层的 .Equals() 方法:

Console.WriteLine(UnboxedInt.Equals(BoxedShort)); //returns false

它返回错误。起初,它似乎在进行引用检查,因为短裤被装在一个对象中。但是,当我将未装箱的短裤与装箱的短裤进行比较时,它返回 true:

Console.WriteLine(object.Equals(BoxedShort, UnboxedShort)); //returns true

如果类型未知,我可以进行比较的唯一方法是使用 Convert.ChangeType():

Console.WriteLine(UnboxedInt == (int)Convert.ChangeType(BoxedShort, typeof(int))); //returns true
Console.WriteLine(object.Equals(UnboxedInt, Convert.ChangeType(BoxedShort, typeof(int)))); //returns true

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

那么,在 boxed short 和 unboxed short 相等,boxed short 和 unboxed int 不相等,但 unboxed short 和 unboxed int 相等的框架中到底发生了什么?

更新

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

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

我会稍等一下,看看是否有其他答案出现。

最佳答案

一切都有合乎逻辑的解释。

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

当您有一个装箱值并使用 Equals 方法时,情况就不同了,因为 Equals 实现通常只检查相同的类型而不检查性能。这是从 reference source 中获取的 Int32 的实现。 :

public override bool Equals(Object obj) {
    if (!(obj is Int32)) {
        return false;
    }
    return m_value == ((Int32)obj).m_value;
}

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

由于没有简单的方法可以在不知道类型的情况下对值进行拆箱,因此您确实应该使用转换。所有基本类型都实现了 IConvertible,它允许将装箱值转换为所需类型(如果可能)。

但是,您实际上应该使用 Convert.ToInt32 而不是 Convert.ChangeType像这样

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

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

关于c# - 当装箱类型未知时检查不同类型的装箱值类型是否相等,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34993599/

相关文章:

c# - 如何创建一个动态相等实现,您可以在其中传递要比较的属性名称?

Agda 中的平等 - 不相关的参数

c# - 有关结构实现接口(interface)时发生的情况的详细信息

f# - 如何为受歧视的联盟覆盖 .Equals() ?

c# - 在 Windows 窗体 C# 中随机生成冒泡排序算法的数字而不重复?

c# - ASP.eventtracker_aspx' 不包含定义

c# - 如何: Representing NULL in SQLite queries

c# - CLR 顺序结构布局 : aligning and size

.net - 对值类型调用方法是否会导致 .NET 中装箱?

c# - 如果在 Controller 中找不到操作,MVC 路由到索引?