请考虑以下代码。 's'被拆分两次到两个不同的数组。
string s = "1,2,3";
string[] arr = s.Split(',');
string[] arr2 = s.Split(',');
foreach (..)
{ // do something
}
在发布模式下编译时,IL看起来像这样,所以Split实际上被调用了两次。有没有理由不进行优化?
IL_0008: newarr [mscorlib]System.Char
IL_000d: stloc.s CS$0$0000
IL_000f: ldloc.s CS$0$0000
IL_0011: ldc.i4.0
IL_0012: ldc.i4.s 44
IL_0014: stelem.i2
IL_0015: ldloc.s CS$0$0000
IL_0017: callvirt instance string[] [mscorlib]System.String::Split(char[])
IL_001c: stloc.1
IL_001d: ldloc.0
IL_001e: ldc.i4.1
IL_001f: newarr [mscorlib]System.Char
IL_0024: stloc.s CS$0$0001
IL_0026: ldloc.s CS$0$0001
IL_0028: ldc.i4.0
IL_0029: ldc.i4.s 44
IL_002b: stelem.i2
IL_002c: ldloc.s CS$0$0001
IL_002e: callvirt instance string[] [mscorlib]System.String::Split(char[])
答案 0 :(得分:2)
将评论提炼为答案:
通常,编译器对方法的内容没有特别的了解 - 即使它could分析当前的实现,也无法知道该实现是否会在重要细节中发生变化。
您认为编译器可以执行的优化的两个最明显的问题是确定性和副作用的存在。
确定性 - 即使在没有任何(明显的)共享状态的情况下,也无法保证对同一函数的两次连续调用会产生相同的结果。
副作用 - 有问题的函数(或它调用的函数)可能产生可见的副作用 - 即使只是增加一个调用计数器 - 这样调用一次或两次会产生不同的整体效果。
现在,有时,编译器可以提取我们自己无法做到的技巧 - 例如它可以知道,对Split()
的两次连续调用,使用一个本地引用,无法分配更可见的引用的副本,应该产生相同的结果。但这是令人难以置信的特定优化,可能不值得进行工程设计。
在 general 中,编译器没有比方法签名更多的知识。而且,在.NET的当前版本中,方法签名不提供关于确定性和副作用的信息。