c# - 在循环内部或外部声明一个变量更好吗?

标签 c# .net 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();
}

或者:

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

    variable2Type baa = new Baa(); 
    baa.main = foo; 
    baa.Do();
}

问题是:1 case 和 2 case 哪个更快? 差异是微不足道的吗?在实际应用中是否一样? 这可能是一个微优化,但我真的很想知道哪个更好。

最佳答案

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

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);
  }
}

我特意选择了值类型和引用类型以防影响事情。现在,他们的 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
}

如您所见,除了编译器碰巧选择的堆栈顺序之外——这也可能是不同的顺序——它绝对没有影响。反过来,确实没有任何一个人可以充分利用另一个人没有提供的抖动。

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

在我的Method1()中,xsb的作用域是foreach,无法访问有意或无意地在它之外。

在我的 Method2() 中,xsb 在编译时无法在 中可靠地赋值>foreach(编译器不知道foreach至少会执行一个循环),所以禁止使用。

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

可以但是在foreach 之外分配和使用x 和/或sb。通常我会说大多数时候这可能是糟糕的范围界定,所以我更喜欢 Method1,但我可能有一些合理的理由想要引用它们(如果它们不是更现实的话可能未分配),在这种情况下,我会选择 Method2

不过,这仍然是每个代码如何扩展或不扩展的问题,而不是编写的代码的差异。真的,没有区别。

关于c# - 在循环内部或外部声明一个变量更好吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8535846/

相关文章:

perl - for 循环的范围问题

c# - 呈现 ModalView 时出现 NSInvalidArgumentException

c# - 如何通过 UserControl 参数管理控件的宽度?

c# - .NET 运行时尝试加载 FSharp.Core 4.3.0,即使所有项目都引用 4.3.1

.net - 我什么时候需要调用 ReleaseComObject?

perl - 嵌套函数调用范围和语法

session - 创建 session 时在Grails中创建 session 变量

c# - 无法将 HashSet 转换为 IReadOnlyCollection

C# - 是否有 32 位 float 数学库?

c# - 如何在 C# 中监视和重新启动引发异常的任务?