【问题标题】:Boxing, a thing of the past?拳击,已成为过去?
【发布时间】:2010-08-17 14:29:17
【问题描述】:

这样做有什么意义吗?

public static void Write<T>(T value)
{
    textWriter.Write(value.ToString());
}

...应该这样:

public static void Write(object value)
{
    textWriter.Write(value.ToString());
}

抛开明显的 null 取消引用的可能性,如果我在哪里使用这个方法写很多值类型不会更好,因为它会有自己的 write 方法版本来调用,或者它只是会因为生成大量附加代码而使二进制文件膨胀吗?

这样的事情对性能的影响可能可以忽略不计,但我很好奇,它比为 BCL 中的每个值类型提供重载要紧凑得多,就像 BCL 中的大多数编写者已经做的那样。

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    据我了解,在这两种情况下都会发生拳击。

    后者很明显,因为值已经被装箱了。

    前者不太明显,但由于在 valuetype 上调用虚拟方法,因此需要将其装箱以执行 callvirt

    编辑:我刚刚检查了发出的 IL,在一般情况下没有显式装箱。不过,有些东西敲响了。

    编辑 2: 我可能对使用接口的情况感到困惑。显然发生了拳击。

    编辑 3: 如果 ToString() 在值类型中未被覆盖,则确实会发生装箱。

    我从 ECMA-335 第 3 部分第 25 页得到这个(只注意最后一种情况):

    如果 thisType 是一个值类型并且 thisType 没有实现方法 然后 ptr 被取消引用,装箱,并且 作为“this”指针传递给 方法的callvirt

    最后一种情况只发生在 方法在System.Object 上定义, System.ValueTypeSystem.Enum 和 未被 thisType 覆盖。在这个 最后一种情况,拳击会导致 要制作的原始对象, 然而,由于所有方法 System.ObjectSystem.ValueTypeSystem.Enum不要修改状态 对象,这个事实不可能 检测到。

    编辑 4: Here is a similar question on SO.

    【讨论】:

    • 所有方法调用都是虚拟的吗?我怀疑只有界面情况才能使拳击成为必要,@Edit 3 现在这有点令人惊讶。我认为它是相反的,虽然值类型有点不同,因为你不能有继承,因此值类型上真的不能有虚拟方法,顺便说一句,这是一个很好的答案!
    【解决方案2】:

    是的,在大多数情况下它会避免拳击。不过,我不希望这在大多数情况下会产生任何显着的性能差异。正如你自己所说,它可能可以忽略不计。

    我认为它确实在可读性方面增加了一些负担。我怀疑大多数人在尝试理解通用方法时比他们使用object 的简单方法时有更多的认知负担。谁将使用这种方法,他们对泛型是否足够满意以至于不会打扰他们?

    【讨论】:

      【解决方案3】:
      • 虚拟通话可能需要拳击(正如 leppie 已经说过的)
      • 泛型在源代码中更紧凑,但不是在 JIT 代码中

      考虑性能:

      • 泛型在一定程度上减小了二进制大小。 JIT 时间和代码位置保持不变,因为无论如何只需要重载
      • 如果该方法不像示例中那样简单,则装箱解决方案将涉及更少的 JIT 调用并减少代码大小(从而也提高了局部性)。这是一种难以衡量的累积效应,除非在极端情况下。

      要点是:没有一种适合所有情况的完美方法。

      【讨论】:

      • 实际上,我刚刚发现并非在所有情况下都需要装箱(例如,在值类型中覆盖了虚拟方法):) 请参阅我的修改。
      • 啊……有趣。添加了黄鼠狼的话;)
      猜你喜欢
      • 2010-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-27
      相关资源
      最近更新 更多