当前位置:  开发笔记 > 编程语言 > 正文

在FOR循环中声明变量

如何解决《在FOR循环中声明变量》经验,为你挑选了3个好方法。

生产中发生了一个奇怪的错误,我被要求调查.
问题被追溯到在For循环中声明的几个变量,而不是在每次迭代时初始化.已经假设由于其声明的范围,它们将在每次迭代时"重置".
有人可以解释为什么他们不会)?
(我的第一个问题,真的很期待回复.)
下面的例子显然不是有问题的代码但是反映了这个场景:
请原谅代码示例,它在编辑器预览中看起来很好吗?

for (int i =0; i< 10; i++)
{
    decimal? testDecimal;
    string testString;

    switch( i % 2  )
    {
        case 0:
        testDecimal = i / ( decimal ).32;
        testString = i.ToString();
            break;
        default:
            testDecimal = null;
            testString = null;
            break;
    }

    Console.WriteLine( "Loop {0}: testDecimal={1} - testString={2}", i, testDecimal , testString );
}

编辑:

对不起,不得不急于解决托儿问题.问题在于,prod代码是switch语句很大,并且在一些"case"中检查了一个类'属性,就像if(myObject.Prop!= null)然后testString = myObject.Stringval. ..在开关的末尾,(外部)正在检查testString == null但它保持了最后一次迭代的值,因此不会为null,因为编码器假定变量在循环内声明.
对不起,如果我的问题和示例有点过时,我接到了关于日间护理的电话,因为我正在敲打它.我应该提到我在循环中比较了来自两个变量的IL.那么,普遍认为"显然变量不会在每个循环中重新初始化"吗?
更多信息,变量WHERE在每次迭代时被初始化,直到有人对ReSharper过度热情指出"值从未使用过"并将其删除.


编辑:

伙计们,谢谢大家.作为我的第一篇文章,我看到将来我应该有多清楚.我的意外变量分配的原因可以放在一个没有经验的开发人员做ReSharper告诉他的所有事情,而不是在他对整个解决方案运行"代码清理"之后运行任何单元测试.在VSS中查看此模块的历史记录,我看到变量在循环外部声明的位置以及在每次迭代时初始化的位置.有问题的人希望他的ReSharper显示"全部绿色",以便"将他的变量更接近分配"然后"删除冗余任务"!我认为他不会再这样做了......现在花个周末去参加他错过的所有单元测试!
如何将问题标记为已回答?



1> Marc Gravell..:

大多数情况下,无论是在循环内部还是外部声明变量都无关紧要; 明确分配规则确保无关紧要.在调试器中,您可能偶尔会看到旧值(即,如果在分配之前查看断点中的变量),但静态分析证明这不会影响执行代码.每个循环都不会重置变量,因为显然没有必要.

在IL级别,**通常*变量仅为方法声明一次 - 循环内的位置对我们程序员来说只是一种便利.

然而,有一个重要的例外; 在捕获变量的任何时候,范围规则变得更加复杂.例如(2秒):

        int value;
        for (int i = 0; i < 5; i++)
        {
            value = i;
            ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(value); });
        }
        Console.ReadLine();

非常不同的:

        for (int i = 0; i < 5; i++)
        {
            int value = i;
            ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(value); });
        }
        Console.ReadLine();

如在第二示例中的"值"是真正的每个实例,因为它被捕获.这意味着第一个例子可能显示(例如)"4 4 4 4 4",其中 - 第二个例子将显示0-5(以任何顺序) - 即"1 2 5 3 4".

那么:是否涉及原始代码?任何带有lambda,匿名方法或LINQ查询的内容都符合条件.



2> Grant Wagner..:

摘要

比较生成的IL用于在循环内声明变量和生成的IL用于声明循环外的变量,证明两种样式的变量声明之间没有性能差异.(生成的IL实际上是相同的.)


这是原始源,据说使用"更多资源",因为变量是在循环内声明的:

using System;

class A
{
    public static void Main()
    {
        for (int i =0; i< 10; i++)
        {
            decimal? testDecimal;
            string testString;
            switch( i % 2  )
            {
                case 0:
                    testDecimal = i / ( decimal ).32;
                    testString = i.ToString();
                    break;
                default:
                    testDecimal = null;
                    testString = null;
                    break;
            }

            Console.WriteLine( "Loop {0}: testDecimal={1} - testString={2}", i, testDecimal , testString );
        }
    }
}

以下是来自低效声明源的IL:

.method public hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 8
    .locals init (
        [0] int32 num,
        [1] valuetype [mscorlib]System.Nullable`1 nullable,
        [2] string str,
        [3] int32 num2,
        [4] bool flag)
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: br.s L_0061
    L_0005: nop 
    L_0006: ldloc.0 
    L_0007: ldc.i4.2 
    L_0008: rem 
    L_0009: stloc.3 
    L_000a: ldloc.3 
    L_000b: ldc.i4.0 
    L_000c: beq.s L_0010
    L_000e: br.s L_0038
    L_0010: ldloca.s nullable
    L_0012: ldloc.0 
    L_0013: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int32)
    L_0018: ldc.i4.s 0x20
    L_001a: ldc.i4.0 
    L_001b: ldc.i4.0 
    L_001c: ldc.i4.0 
    L_001d: ldc.i4.2 
    L_001e: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8)
    L_0023: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Division(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
    L_0028: call instance void [mscorlib]System.Nullable`1::.ctor(!0)
    L_002d: nop 
    L_002e: ldloca.s num
    L_0030: call instance string [mscorlib]System.Int32::ToString()
    L_0035: stloc.2 
    L_0036: br.s L_0044
    L_0038: ldloca.s nullable
    L_003a: initobj [mscorlib]System.Nullable`1
    L_0040: ldnull 
    L_0041: stloc.2 
    L_0042: br.s L_0044
    L_0044: ldstr "Loop {0}: testDecimal={1} - testString={2}"
    L_0049: ldloc.0 
    L_004a: box int32
    L_004f: ldloc.1 
    L_0050: box [mscorlib]System.Nullable`1
    L_0055: ldloc.2 
    L_0056: call void [mscorlib]System.Console::WriteLine(string, object, object, object)
    L_005b: nop 
    L_005c: nop 
    L_005d: ldloc.0 
    L_005e: ldc.i4.1 
    L_005f: add 
    L_0060: stloc.0 
    L_0061: ldloc.0 
    L_0062: ldc.i4.s 10
    L_0064: clt 
    L_0066: stloc.s flag
    L_0068: ldloc.s flag
    L_006a: brtrue.s L_0005
    L_006c: ret 
}

这是声明循环外变量的源:

using System;

class A
{
    public static void Main()
    {
        decimal? testDecimal;
        string testString;

        for (int i =0; i< 10; i++)
        {
            switch( i % 2  )
            {
                case 0:
                    testDecimal = i / ( decimal ).32;
                    testString = i.ToString();
                    break;
                default:
                    testDecimal = null;
                    testString = null;
                    break;
            }

            Console.WriteLine( "Loop {0}: testDecimal={1} - testString={2}", i, testDecimal , testString );
        }
    }
}

这是IL声明循环外的变量:

.method public hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 8
    .locals init (
        [0] valuetype [mscorlib]System.Nullable`1 nullable,
        [1] string str,
        [2] int32 num,
        [3] int32 num2,
        [4] bool flag)
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.2 
    L_0003: br.s L_0061
    L_0005: nop 
    L_0006: ldloc.2 
    L_0007: ldc.i4.2 
    L_0008: rem 
    L_0009: stloc.3 
    L_000a: ldloc.3 
    L_000b: ldc.i4.0 
    L_000c: beq.s L_0010
    L_000e: br.s L_0038
    L_0010: ldloca.s nullable
    L_0012: ldloc.2 
    L_0013: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Implicit(int32)
    L_0018: ldc.i4.s 0x20
    L_001a: ldc.i4.0 
    L_001b: ldc.i4.0 
    L_001c: ldc.i4.0 
    L_001d: ldc.i4.2 
    L_001e: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8)
    L_0023: call valuetype [mscorlib]System.Decimal [mscorlib]System.Decimal::op_Division(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
    L_0028: call instance void [mscorlib]System.Nullable`1::.ctor(!0)
    L_002d: nop 
    L_002e: ldloca.s num
    L_0030: call instance string [mscorlib]System.Int32::ToString()
    L_0035: stloc.1 
    L_0036: br.s L_0044
    L_0038: ldloca.s nullable
    L_003a: initobj [mscorlib]System.Nullable`1
    L_0040: ldnull 
    L_0041: stloc.1 
    L_0042: br.s L_0044
    L_0044: ldstr "Loop {0}: testDecimal={1} - testString={2}"
    L_0049: ldloc.2 
    L_004a: box int32
    L_004f: ldloc.0 
    L_0050: box [mscorlib]System.Nullable`1
    L_0055: ldloc.1 
    L_0056: call void [mscorlib]System.Console::WriteLine(string, object, object, object)
    L_005b: nop 
    L_005c: nop 
    L_005d: ldloc.2 
    L_005e: ldc.i4.1 
    L_005f: add 
    L_0060: stloc.2 
    L_0061: ldloc.2 
    L_0062: ldc.i4.s 10
    L_0064: clt 
    L_0066: stloc.s flag
    L_0068: ldloc.s flag
    L_006a: brtrue.s L_0005
    L_006c: ret 
}

我将分享这个秘密,除了.locals init ( ... )指定的顺序之外,IL完全相同.循环内的DECLARING变量导致NO ADDITIONAL IL.


格兰特,好的分析但有点冗长.您可能想要使用一个不会产生大量IL的简单示例.

3> Konrad Rudol..:

你不应该把声明放在for循环中.它吸收了额外的资源来反复创建变量,当你应该做的只是在每次迭代时清除变量.

不,它没有!应该完成与你的建议完全相反的建议.但即使重置变量更有效,在更严格的范围内声明变量也要清楚得多.并且随着时间的推移,清晰度胜过微优化(几乎).此外,一个变量,一个用法.不要不必要地重用变量.

也就是说,变量不会在此重置或重新初始化 - 实际上,它们甚至不是由C#初始化的!要解决这个问题,只需初始化它们即可完成.

推荐阅读
农大军乐团_697
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有