C#是否在循环/ lambda语句中自动优化代码?

Ben*_* E. 14 c#

例如,在Javascript中,强烈建议人们在循环之外放置函数调用以获得更好的性能:

var id = someIdType.ToString();
someList.Where(a => a.id == id) ...
Run Code Online (Sandbox Code Playgroud)

C#怎么样?同样的情况还是编译器/运行时采用内部优化/缓存?

someList.Where(a => a.id == someIdType.ToString()) ...
Run Code Online (Sandbox Code Playgroud)

可能是一个菜鸟问题并且之前一直有人问过,但是找不到参考.

Ale*_*x F 14

C#代码:

List<string> list = new List<string>();
list.Where(a => a == typeof(String).ToString());
Run Code Online (Sandbox Code Playgroud)

MSIL中的Lambda表达式,调试配置:

.method private hidebysig static bool  '<Main>b__0'(string a) cil managed
{
  .custom instance void     [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       26 (0x1a)
  .maxstack  2
  .locals init ([0] bool CS$1$0000)
  IL_0000:  ldarg.0
  IL_0001:  ldtoken    [mscorlib]System.String
  IL_0006:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_000b:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0010:  call       bool [mscorlib]System.String::op_Equality(string,
                                                             string)
  IL_0015:  stloc.0
  IL_0016:  br.s       IL_0018
  IL_0018:  ldloc.0
  IL_0019:  ret
} // end of method Program::'<Main>b__0'
Run Code Online (Sandbox Code Playgroud)

MSIL中的Lambda表达式,发布配置:

.method private hidebysig static bool  '<Main>b__0'(string a) cil managed
{
  .custom instance void     [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldtoken    [mscorlib]System.String
  IL_0006:  call       class [mscorlib]System.Type     [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_000b:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0010:  call       bool [mscorlib]System.String::op_Equality(string,
                                                             string)
  IL_0015:  ret
} // end of method Program::'<Main>b__0'
Run Code Online (Sandbox Code Playgroud)

两个版本都调用typeof(String).ToString()),每次迭代都会调用此lambda.在IL级别上没有优化,JIT编译不会在这里添加任何内容.原因是:功能可能有副作用.

  • 查看MSIL没有帮助,代码提升是由[抖动执行的优化](http://stackoverflow.com/a/4045073/17034)生成的机器代码. (7认同)
  • 我不认为这是必然的/特别是由于函数可能有副作用的事实...例如,在lambda内部访问的成员可能是一个字段,而不是一个函数......即使字段访问确实**不**有副作用,它将**不会被缓存..因此把它作为"原因是:功能可能有副作用"并不完全准确..所以我只是说..编译器/运行时不缓存它..为什么?谁知道?可能是他们从未考虑过..可能是他们有10个理由,包括副作用功能......可能他们发现这个功能一般来说ROI很低. (5认同)

Vik*_*pta 5

lambda将为列表的每个元素执行.因此代码someIdType.ToString()将为每个元素执行.我认为编译器或运行时不会为您缓存它.(AFAIK someIdType将被关闭,但不是.ToString())

编辑: 最初的问题只是关于"是吗?",而不是"为什么?",但仍然有几个评论和其他答案试图回答/演示"为什么?".

对"为什么?"这么感兴趣?我正在编辑我的答案,陈述我的版本"为什么?".也就是说,如果你看一下C#规范,对于任何相关场景,规范都会谈到capturing the variable.. 不是 capturing the expression.这就是编译器行为方式的原因.因为它不在规范中.为什么它不在规范中,是C#设计团队可以回答的问题.如果feature of capturing expressions需要考虑的话,其余的是推测,部分或全部可能有或没有优点.

  • 编译器不能也不会缓存它,因为在C#函数中不是纯函数,即它们可以有副作用或在相同的参数上返回不同的值(查看`Random.Next()`) (3认同)