我正在通过C# (第4版)阅读“CLR”一书,不是作为C#的新手,而是作为一个懂语言的人,试图提高我对CLR的基本功能的理解。
无论如何,在本书中,在讨论值类型的装箱/取消装箱时,给出了一个示例(pg127-131),该示例的结尾是对Console.WriteLine的调用,并将值类型连接到作为参数传递的字符串。
这本书解释了装箱和取消装箱/复制操作会造成开销,我已经知道了这一点,但它随后指出,可以通过在传入的值类型上运行.ToString()来优化示例。
我创建了一个示例程序并编译了它,然后使用ILDASM检查它生成的IL。ToString的版本本质上是相同的,但是将"box“指令替换为对ToString的”调用“(没有震惊)。
我在一个100000次运行的循环中对代码进行了基准测试,没有差别(它波动于哪一个运行得更快)。我意识到,其他因素在基准测试(缓存等)中起作用,但顺便说一句,这本书解释说,即使是在一个幼稚的基准中,我也希望在避免使用“盒子”指令时会看到显著的不同。
仅仅是调用一个函数不是更好吗?在ToString中是否有一种拳击操作可以抵消这些好处,而这本书却是错的呢?有人能解释一下这件事吗?
以下是两个ILDASM读物,供参考:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 24 (0x18)
.maxstack 2
.locals init (int32 V_0)
IL_0000: ldc.i4.4
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: box [mscorlib]System.Int32
IL_0008: ldstr "."
IL_000d: call string [mscorlib]System.String::Concat(object,
object)
IL_0012: call void [mscorlib]System.Console::WriteLine(string)
IL_0017: ret
} // end of method Program::Main.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 25 (0x19)
.maxstack 2
.locals init (int32 V_0)
IL_0000: ldc.i4.4
IL_0001: stloc.0
IL_0002: ldloca.s V_0
IL_0004: call instance string [mscorlib]System.Int32::ToString()
IL_0009: ldstr "."
IL_000e: call string [mscorlib]System.String::Concat(string,
string)
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: ret
} // end of method Program::Main发布于 2015-03-11 19:42:30
CLR可能会内联对string.Concat(object,object)的调用,这将产生与“优化”版本相同的代码。请注意,C#编译器将把许多这类优化留给CLR,因为它有更好的工具可供使用。
除了一对空检查(这将被优化出)之外,它只调用string.Concat(left.ToString(),right.ToString()),这将被简化为string.Concat(left,right.ToString()),因为CLR会看到ToString()只返回this。
因此,在这两种情况下,执行的代码可能是相同的。
发布于 2015-03-11 19:41:13
您忽略了这样一个事实,即String.Concat将对提供的object参数在内部调用ToString:
public static String Concat(Object arg0, Object arg1) {
Contract.Ensures(Contract.Result<String>() != null);
Contract.EndContractBlock();
if (arg0 == null)
{
arg0 = String.Empty;
}
if (arg1==null) {
arg1 = String.Empty;
}
return Concat(arg0.ToString(), arg1.ToString());
}因此,call指令无论如何都会存在,但是它隐藏在Concat方法调用中。
调用ToString会选择不同的Concat重载,并且这个重载不会在内部调用ToString。
IL_000d: call string [mscorlib]System.String::Concat(object, object)与
IL_000e: call string [mscorlib]System.String::Concat(string, string)https://stackoverflow.com/questions/28995660
复制相似问题