C# 在此场景中声明和使用变量的有效方法

C# 在此场景中声明和使用变量的有效方法,c#,optimization,coding-style,C#,Optimization,Coding Style,在这种情况下,哪种更好的/优化的方式声明和使用变量 int i; for(i = 0; i < 10; i++) Console.WriteLine(i); for(i = 0; i < 100; i++) Console.WriteLine(i); inti; 对于(i=0;i

在这种情况下,哪种更好的/优化的方式声明和使用变量

int i;

for(i = 0; i < 10; i++)
    Console.WriteLine(i);

for(i = 0; i < 100; i++)
    Console.WriteLine(i);
inti;
对于(i=0;i<10;i++)
控制台写入线(i);
对于(i=0;i<100;i++)
控制台写入线(i);

for(int i=0;i<10;i++)
控制台写入线(i);
对于(int i=0;i<100;i++)
控制台写入线(i);

从性能的角度来看,这是不相关的

首先,即使在第二个示例中,运行时重新使用单个变量的可能性也相当高

下一步,即使它不这样做,为堆栈分配一个额外的int,然后在以后回收它,在性能上几乎没有任何成本。它使该方法的内存占用空间增加了4字节;就这样。如果这实际上是您的问题(即堆栈空间不足),那么您需要解决更大的问题,而这不是合适的解决方法;这可能意味着您应该将递归函数转换为非递归函数


你应该做任何你认为可读性最好、写起来更容易、最不可能引起错误或问题的事情。对我来说,这几乎总是第二种情况,但如果你更喜欢写出第一种情况(你的团队也同意),那么这完全取决于你,请记住,性能在这里完全不是一个因素。

从性能的角度来看,这是不相关的

首先,即使在第二个示例中,运行时重新使用单个变量的可能性也相当高

下一步,即使它不这样做,为堆栈分配一个额外的int,然后在以后回收它,在性能上几乎没有任何成本。它使该方法的内存占用空间增加了4字节;就这样。如果这实际上是您的问题(即堆栈空间不足),那么您需要解决更大的问题,而这不是合适的解决方法;这可能意味着您应该将递归函数转换为非递归函数


你应该做任何你认为可读性最好、写起来更容易、最不可能引起错误或问题的事情。对我来说,这几乎总是第二种情况,但如果你更喜欢写出第一种情况(你的团队也同意),那么这完全取决于你,请记住,性能在这里完全不是一个因素。

在有些情况下,你需要For循环之外的计数器变量值

int i;
for (i = 0; i < N; i++) {
    if (SomeCondition(i)) {
        break;
    }
}
DoSomeThingWith(i);
inti;
对于(i=0;i
。。。然后在for循环之外声明它。在所有其他情况下,我会在for循环中声明它,因为它使这个变量的意图更加清晰

for (int i = 0; i < N; i++) {
    ...
}
for(int i=0;i

这里很明显,这个变量在循环之外不会有一个有意义的值(它的作用域无论如何只限于循环)。很明显,它将仅用于此迭代目的,而不用于其他目的。

在某些情况下,您需要for循环外部的计数器变量值

int i;
for (i = 0; i < N; i++) {
    if (SomeCondition(i)) {
        break;
    }
}
DoSomeThingWith(i);
inti;
对于(i=0;i
。。。然后在for循环之外声明它。在所有其他情况下,我会在for循环中声明它,因为它使这个变量的意图更加清晰

for (int i = 0; i < N; i++) {
    ...
}
for(int i=0;i

这里很明显,这个变量在循环之外不会有一个有意义的值(它的作用域无论如何只限于循环)。很明显,它将仅用于此迭代目的,而不用于其他目的。

如果您查看MSIL,您可以看到差异在于变量声明:

    .method public hidebysig static 
    void task1 () cil managed 
{
    .locals init (
        [0] int32 i
    )

    IL_0000: ldc.i4.0
    IL_0001: stloc.0
    IL_0002: br.s IL_000e
    .loop
    {
        IL_0004: ldloc.0
        IL_0005: call void [mscorlib]System.Console::WriteLine(int32)
        IL_000a: ldloc.0
        IL_000b: ldc.i4.1
        IL_000c: add
        IL_000d: stloc.0

        IL_000e: ldloc.0
        IL_000f: ldc.i4.s 10
        IL_0011: blt.s IL_0004
    }

    IL_0013: ldc.i4.0
    IL_0014: stloc.0
    IL_0015: br.s IL_0021
    .loop
    {
        IL_0017: ldloc.0
        IL_0018: call void [mscorlib]System.Console::WriteLine(int32)
        IL_001d: ldloc.0
        IL_001e: ldc.i4.1
        IL_001f: add
        IL_0020: stloc.0

        IL_0021: ldloc.0
        IL_0022: ldc.i4.s 100
        IL_0024: blt.s IL_0017
    }

    IL_0026: ret
}
第二种情况:

    .method public hidebysig static 
    void task2 () cil managed 
{
    .locals init (
        [0] int32 i,
        [1] int32 i
    )

    IL_0000: ldc.i4.0
    IL_0001: stloc.0
    IL_0002: br.s IL_000e
    .loop
    {
        IL_0004: ldloc.0
        IL_0005: call void [mscorlib]System.Console::WriteLine(int32)
        IL_000a: ldloc.0
        IL_000b: ldc.i4.1
        IL_000c: add
        IL_000d: stloc.0

        IL_000e: ldloc.0
        IL_000f: ldc.i4.s 10
        IL_0011: blt.s IL_0004
    }

    IL_0013: ldc.i4.0
    IL_0014: stloc.1
    IL_0015: br.s IL_0021
    .loop
    {
        IL_0017: ldloc.1
        IL_0018: call void [mscorlib]System.Console::WriteLine(int32)
        IL_001d: ldloc.1
        IL_001e: ldc.i4.1
        IL_001f: add
        IL_0020: stloc.1

        IL_0021: ldloc.1
        IL_0022: ldc.i4.s 100
        IL_0024: blt.s IL_0017
    }

    IL_0026: ret
}

差异是一个局部声明。并将其加载到内存中。在第一种情况下,它会加载两次变量。

如果您查看MSIL,您可以看到不同之处是变量声明:

    .method public hidebysig static 
    void task1 () cil managed 
{
    .locals init (
        [0] int32 i
    )

    IL_0000: ldc.i4.0
    IL_0001: stloc.0
    IL_0002: br.s IL_000e
    .loop
    {
        IL_0004: ldloc.0
        IL_0005: call void [mscorlib]System.Console::WriteLine(int32)
        IL_000a: ldloc.0
        IL_000b: ldc.i4.1
        IL_000c: add
        IL_000d: stloc.0

        IL_000e: ldloc.0
        IL_000f: ldc.i4.s 10
        IL_0011: blt.s IL_0004
    }

    IL_0013: ldc.i4.0
    IL_0014: stloc.0
    IL_0015: br.s IL_0021
    .loop
    {
        IL_0017: ldloc.0
        IL_0018: call void [mscorlib]System.Console::WriteLine(int32)
        IL_001d: ldloc.0
        IL_001e: ldc.i4.1
        IL_001f: add
        IL_0020: stloc.0

        IL_0021: ldloc.0
        IL_0022: ldc.i4.s 100
        IL_0024: blt.s IL_0017
    }

    IL_0026: ret
}
第二种情况:

    .method public hidebysig static 
    void task2 () cil managed 
{
    .locals init (
        [0] int32 i,
        [1] int32 i
    )

    IL_0000: ldc.i4.0
    IL_0001: stloc.0
    IL_0002: br.s IL_000e
    .loop
    {
        IL_0004: ldloc.0
        IL_0005: call void [mscorlib]System.Console::WriteLine(int32)
        IL_000a: ldloc.0
        IL_000b: ldc.i4.1
        IL_000c: add
        IL_000d: stloc.0

        IL_000e: ldloc.0
        IL_000f: ldc.i4.s 10
        IL_0011: blt.s IL_0004
    }

    IL_0013: ldc.i4.0
    IL_0014: stloc.1
    IL_0015: br.s IL_0021
    .loop
    {
        IL_0017: ldloc.1
        IL_0018: call void [mscorlib]System.Console::WriteLine(int32)
        IL_001d: ldloc.1
        IL_001e: ldc.i4.1
        IL_001f: add
        IL_0020: stloc.1

        IL_0021: ldloc.1
        IL_0022: ldc.i4.s 100
        IL_0024: blt.s IL_0017
    }

    IL_0026: ret
}

差异是一个局部声明。并将其加载到内存中。在第一种情况下,它会加载两次变量。

Related@Servy-dupe稍有不同,因为它只查看使用该变量的一个循环,而不是在多个循环中声明一个变量是否有任何好处。一个有趣的补充是,如果您的循环包含一个匿名委托,并且在该循环上有一个闭包局部变量,前者的行为与后者不同,但仅在(当前)最新版本的C#编译器中。这是由于最近的一次突破性更改,该更改是为了防止由于关闭不断变化的循环变量而导致的常见错误,而该变量直观地显示为具有“内部”作用域。@DanBryant No。这是一个
for
循环,而不是
foreach
循环。变量的范围在不同版本之间没有改变。@Servy,啊,很高兴知道,我认为更改影响了这两种循环类型。Related@Servy-dupe略有不同,因为它只关注使用变量的一个循环,而不是声明一个变量用于多个循环是否有任何好处。一个有趣的补充是,如果您的循环包含一个匿名委托,并且在该循环上有一个闭包局部变量,前者的行为与后者不同,但仅在(当前)最新版本的C#编译器中。这是由于最近的一次突破性更改,该更改是为了防止由于关闭不断变化的循环变量而导致的常见错误,而该变量直观地显示为具有“内部”作用域。@DanBryant No。这是一个
for
循环,而不是
foreach
循环。变量的范围在不同版本之间没有更改。@Servy,啊,很高兴知道,我认为更改影响了两种循环类型。添加一个额外的局部变量对性能有什么影响?