String.Format(...)中的Boxing和Unboxing ......以下是合理化的吗?

Car*_*los 23 .net c# boxing

我正在做一些关于装箱/拆箱的阅读,事实证明,如果你做一个普​​通的String.Format()你在你的object[]参数列表中有一个值类型,它将导致一个装箱操作.例如,如果您正在尝试打印出整数的值并执行此操作string.Format("My value is {0}",myVal),则会将其粘贴myVal int到一个框中并在其ToString上运行该函数.

浏览,我发现了这篇文章.

看来你可以简单地通过.ToString在将值传递给string.Format函数之前执行值类型来避免拳击惩罚:string.Format("My value is {0}",myVal.ToString())

  1. 这是真的吗?我倾向于相信作者的证据.
  2. 如果这是真的,为什么编译器不能简单地为你做这个?也许它自2006年以来发生了变化?有人知道吗?(我没有时间/经验来进行整个IL分析)

Mer*_*ham 16

编译器不会为你执行此操作,因为string.Format需要a params Object[].拳击发生是因为转换为Object.

我不认为编译器倾向于特殊情况方法,因此在这种情况下它不会删除装箱.

是的,在许多情况下,如果你ToString()先打电话,编译器就不会做拳击.如果它使用Object我认为仍然需要的实现.

最终,string.Format格式字符串本身的解析将比任何装箱操作慢得多,因此开销可以忽略不计.

  • String.Format("test {0}",someInt)的MSIL有一个框操作,而String.Format的MSIL("test {0}",someInt.ToString())没有.根据经验,我同意,解析格式字符串肯定是缓慢的部分. (3认同)

Mar*_*ell 12

1:是的,只要 value-type覆盖ToString(),所有内置类型都会覆盖.

2:因为规范中没有定义这样的行为,并且params object[](wrt值类型)的正确处理是:拳击

string.Format就像任何其他不透明方法一样; 这样做的事实对编译器来说是不透明的.如果模式包含类似的格式{0:n2}(这需要特定的转换,而不仅仅是ToString()),这在功能上也是不正确的.试图理解模式是不可取的和不可靠的,因为模式可能直到运行时才知道.


csh*_*net 7

通过使用StringBuilderStringWriter构造字符串并使用类型化的重载来避免装箱会更好.

大多数时候拳击应该没什么问题,甚至不值得你去了解它.


Jon*_*nna 6

首先是简单的.编译器不转的原因string.Format("{0}", myVal)string.Format{"{0}", myVal.ToString()),是没有任何理由为什么它应该.应该BlahFooBlahBlah(myVal)变成BlahFooBlahBlah(myVal.ToString())什么?也许这会有相同的效果,但为了更好的性能,但它可能会引入一个错误.编译错误!没有饼干!

除非可以从一般原则中推断出某些东西,否则编译器应该单独留下.

现在有趣的一点IMO:为什么前者导致拳击而后者没有.

对于前者,由于唯一匹配的签名是string.Format(string, object)整数必须转换为要传递给方法的对象(盒装),该方法期望接收字符串和对象.

这个的另一半,为什么不myVal.ToString()包装?

当编译器遇到这段代码时,它具有以下知识:

  1. myVal是一个Int32.
  2. ToString()由Int32定义
  3. Int32是一个值类型,因此:
  4. myVal不可能是空引用*和:
  5. 不可能有更多派生的覆盖 - Int32.ToString()被有效密封.

现在,通常C#编译器callvirt用于所有方法调用有两个原因.首先,有时候你确实希望它成为一个虚拟的电话.第二个是(更有争议的)他们决定禁止对null引用进行任何方法调用,并且callvirt有一个内置的测试.

但在这种情况下,这些都不适用.不能有更多派生类覆盖Int32.ToString(),myVal不能为null.因此它可以引入一个通过无拳击callToString()方法Int32.

这种组合(值不能为空,则方法不能被重写别处)仅参考类型出现常常要少得多,所以编译器不能把它作为多优点然后(它也不会花费高达,因为他们不必被装箱).

如果Int32继承方法实现,则不是这种情况.例如myVal.GetType(),盒子myVal里没有Int32覆盖 - 不可能,它不是虚拟的 - 所以它只能myVal通过装箱作为对象来处理.

事实上,这意味着C#编译器将callvirt用于非虚拟方法,有时call用于虚拟方法,这一点并非毫无讽刺意味.

*请注意,即使是可设置为null的可空整数也与此方面的空引用不同.