在循环内部或外部声明变量是否更好?

The*_*ask 31 .net c# scope

最好做的是:

variable1Type foo; 
variable2Type baa; 

foreach(var val in list) 
{
    foo = new Foo( ... ); 
    foo.x = FormatValue(val); 

    baa = new Baa(); 
    baa.main = foo; 
    baa.Do();
}
Run Code Online (Sandbox Code Playgroud)

要么:

foreach(var val in list) 
{
    variable1Type foo = new Foo( ... ); 
    foo.x = FormatValue(val); 

    variable2Type baa = new Baa(); 
    baa.main = foo; 
    baa.Do();
}
Run Code Online (Sandbox Code Playgroud)

问题是:什么是更快的1例或2例?区别是微不足道的吗?它在实际应用中是否相同?这可能是一个优化微观,但我真的想知道哪个更好.

Jon*_*nna 52

在性能方面,让我们尝试具体的例子:

public void Method1()
{
  foreach(int i in Enumerable.Range(0, 10))
  {
    int x = i * i;
    StringBuilder sb = new StringBuilder();
    sb.Append(x);
    Console.WriteLine(sb);
  }
}
public void Method2()
{
  int x;
  StringBuilder sb;
  foreach(int i in Enumerable.Range(0, 10))
  {
    x = i * i;
    sb = new StringBuilder();
    sb.Append(x);
    Console.WriteLine(sb);
  }
}
Run Code Online (Sandbox Code Playgroud)

我特意选择了值类型和引用类型,以防影响事物.现在,他们的IL:

.method public hidebysig instance void Method1() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 i,
        [1] int32 x,
        [2] class [mscorlib]System.Text.StringBuilder sb,
        [3] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> enumerator)
    L_0000: ldc.i4.0 
    L_0001: ldc.i4.s 10
    L_0003: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, int32)
    L_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    L_000d: stloc.3 
    L_000e: br.s L_002f
    L_0010: ldloc.3 
    L_0011: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
    L_0016: stloc.0 
    L_0017: ldloc.0 
    L_0018: ldloc.0 
    L_0019: mul 
    L_001a: stloc.1 
    L_001b: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_0020: stloc.2 
    L_0021: ldloc.2 
    L_0022: ldloc.1 
    L_0023: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(int32)
    L_0028: pop 
    L_0029: ldloc.2 
    L_002a: call void [mscorlib]System.Console::WriteLine(object)
    L_002f: ldloc.3 
    L_0030: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    L_0035: brtrue.s L_0010
    L_0037: leave.s L_0043
    L_0039: ldloc.3 
    L_003a: brfalse.s L_0042
    L_003c: ldloc.3 
    L_003d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0042: endfinally 
    L_0043: ret 
    .try L_000e to L_0039 finally handler L_0039 to L_0043
}

.method public hidebysig instance void Method2() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 x,
        [1] class [mscorlib]System.Text.StringBuilder sb,
        [2] int32 i,
        [3] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> enumerator)
    L_0000: ldc.i4.0 
    L_0001: ldc.i4.s 10
    L_0003: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, int32)
    L_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    L_000d: stloc.3 
    L_000e: br.s L_002f
    L_0010: ldloc.3 
    L_0011: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
    L_0016: stloc.2 
    L_0017: ldloc.2 
    L_0018: ldloc.2 
    L_0019: mul 
    L_001a: stloc.0 
    L_001b: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_0020: stloc.1 
    L_0021: ldloc.1 
    L_0022: ldloc.0 
    L_0023: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(int32)
    L_0028: pop 
    L_0029: ldloc.1 
    L_002a: call void [mscorlib]System.Console::WriteLine(object)
    L_002f: ldloc.3 
    L_0030: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    L_0035: brtrue.s L_0010
    L_0037: leave.s L_0043
    L_0039: ldloc.3 
    L_003a: brfalse.s L_0042
    L_003c: ldloc.3 
    L_003d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0042: endfinally 
    L_0043: ret 
    .try L_000e to L_0039 finally handler L_0039 to L_0043
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,除了编译器碰巧选择的堆栈顺序 - 它可能也是一个不同的顺序 - 它绝对没有效果.反过来,实际上并没有任何东西可以让抖动充分利用另一个没有给予它的东西.

除此之外,还有一种区别.

在我的Method1(),x并且sb是范围的foreach,并且不能故意或意外地访问它之外.

在我的Method2(),x并且sb在编译时不知道可靠地分配一个值foreach(编译器不知道foreach将执行至少一个循环),因此禁止使用它.

到目前为止,没有真正的区别.

然而,我可以分配和使用x和/或sb外部foreach.作为一项规则,我会说这可能在大多数时候都是差的,所以我很乐意Method1,但我可能有一些明智的理由想要引用它们(如果它们不可能未分配则更现实),其中我会去的Method2.

不过,这是每个代码如何扩展的问题,而不是所写代码的差异.真的,没有区别.

  • +1用于创建测试用例并使用它来提供证据来支持您的声明.做得好. (2认同)