C# 以String.Format(…)格式装箱和取消装箱。。。以下是合理的吗?
我正在阅读一些有关装箱/取消装箱的内容,结果表明,如果执行普通的C# 以String.Format(…)格式装箱和取消装箱。。。以下是合理的吗?,c#,.net,boxing,C#,.net,Boxing,我正在阅读一些有关装箱/取消装箱的内容,结果表明,如果执行普通的String.Format(),在object[]参数列表中有一个值类型,则会导致装箱操作。例如,如果您试图打印一个整数的值并执行string.Format(“我的值是{0}”,myVal),它会将您的myValint粘贴在一个框中,并对其运行ToString函数 浏览 在将值类型传递给string.Format函数:string.Format(“我的值是{0}”,myVal.ToString()) 这是真的吗?我倾向于相信作者的观
String.Format()
,在object[]
参数列表中有一个值类型,则会导致装箱操作。例如,如果您试图打印一个整数的值并执行string.Format(“我的值是{0}”,myVal)
,它会将您的myVal
int
粘贴在一个框中,并对其运行ToString
函数
浏览
在将值类型传递给string.Format函数:string.Format(“我的值是{0}”,myVal.ToString())
编译器不会为您执行此操作,因为
string.Format
接受params对象[]
。装箱是由于转换到对象
而发生的
我不认为编译器倾向于使用特殊的case方法,所以在这种情况下它不会删除装箱
是的,在许多情况下,如果您首先调用ToString()
,编译器确实不会执行装箱。如果它使用来自Object
的实现,我认为它仍然必须使用box
最终,格式字符串本身的
string.Format
解析要比任何装箱操作慢得多,因此开销可以忽略不计。1:是的,只要值类型覆盖ToString()
,所有内置类型都会这样做
2:因为规范中没有定义此类行为,正确处理params对象[]
(wrt值类型)的方法是:装箱
Format就像其他不透明的方法一样;它将这样做的事实对编译器来说是不透明的。如果模式包含像{0:n2}
这样的格式(这需要特定的转换,而不仅仅是ToString()
),那么在功能上也是不正确的。试图理解该模式是不可取的,也是不可靠的,因为在运行时之前可能不知道该模式。string.Format(“我的值是{0}”,myVal)
string.Format("My value is {0}", myVal)<br>
myVal is an object<br><br>
string.Format("My value is {0}",myVal.ToString())<br>
myVal.ToString() is a string<br><br>
myVal是一个对象
Format(“我的值是{0}”,myVal.ToString())
myVal.ToString()是一个字符串
ToString过载,因此编译器无法为您做出决定。为什么不尝试每种方法1亿次左右,看看需要多长时间:
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
int myVal = 6;
sw.Start();
for (int i = 0; i < 100000000; i++)
{
string string1 = string.Format("My value is {0}", myVal);
}
sw.Stop();
Console.WriteLine("Original method - {0} milliseconds", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < 100000000; i++)
{
string string2 = string.Format("My value is {0}", myVal.ToString());
}
sw.Stop();
Console.WriteLine("ToStringed method - {0} milliseconds", sw.ElapsedMilliseconds);
Console.ReadLine();
}
static void Main(字符串[]args)
{
秒表sw=新秒表();
int-myVal=6;
sw.Start();
对于(int i=0;i<100000000;i++)
{
string1=string.Format(“我的值是{0}”,myVal);
}
sw.Stop();
WriteLine(“原始方法-{0}毫秒”,sw.elapsedmillisons);
sw.Reset();
sw.Start();
对于(int i=0;i<100000000;i++)
{
string string2=string.Format(“我的值是{0}”,myVal.ToString());
}
sw.Stop();
WriteLine(“toString方法-{0}毫秒”,sw.elapsedmillisons);
Console.ReadLine();
}
在我的机器上,我发现.toString版本运行的时间大约是原始版本运行时间的95%,因此有一些经验证据表明它有轻微的性能优势。最好通过使用或构造字符串并使用类型化重载来避免装箱
大多数时候,拳击应该不太重要,甚至不值得你注意。首先是简单的一项。编译器没有将
string.Format(“{0}”,myVal)
转换为string.Format{0},myVal.ToString())
的原因是没有理由这样做。它是否应该将BlahFooBlahBlah(myVal)
转换为BlahFooBlahBlah(myVal.ToString())
?也许这会有同样的效果,但为了更好的性能,但很可能会引入错误。糟糕的编译器!没有饼干
除非可以从一般原则中推理出一些东西,否则编译器应该不去管它
现在来看一个有趣的话题:为什么前者会导致拳击,而后者不会
对于前者,由于唯一匹配的签名是string.Format(string,object)
,因此必须将整数转换为一个对象(装箱)以传递给方法,该方法期望接收一个字符串和一个对象
不过,另一半原因是,为什么myVal.ToString()
box不也是如此
当编译器处理这段代码时,它具备以下知识:
callvirt
有两个原因。第一个原因是,有时你确实希望它毕竟是一个虚拟调用。第二个原因是(更有争议的是)他们决定禁止对空引用的任何方法调用,而callvirt
有一个内置测试
但是在这种情况下,这两种方法都不适用。不能有更派生的类重写Int32.ToString(),myVal不能为null。因此,它可以向ToString()
方法引入调用,该方法无需装箱即可传递Int32
这种组合(值不能为null,方法不能在其他地方重写)只会很少出现引用类型,因此编译器无法充分利用它(因为它们不必是