C# 在FOR循环中声明变量
生产过程中出现了一个奇怪的bug,我被要求对此进行研究。C# 在FOR循环中声明变量,c#,scope,C#,Scope,生产过程中出现了一个奇怪的bug,我被要求对此进行研究。这个问题被追踪到For循环中声明的几个变量,而不是在每次迭代中初始化。有人假设,由于它们声明的范围,它们在每次迭代中都会被“重置”有人能解释一下为什么他们不会吗? (我的第一个问题,非常期待回答。) 下面的示例显然不是所讨论的代码,而是反映了以下场景: 请原谅代码示例,它在编辑器预览中看起来不错 for (int i =0; i< 10; i++) { decimal? testDecimal; string test
这个问题被追踪到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 );
}
for(int i=0;i<10;i++)
{
十进制?测试十进制;
字符串测试字符串;
交换机(i%2)
{
案例0:
testDecimal=i/(十进制).32;
testString=i.ToString();
打破
违约:
testDecimal=null;
testString=null;
打破
}
WriteLine(“循环{0}:testDecimal={1}-testString={2}”,i,testDecimal,testString);
}
编辑: 对不起,我得赶出去处理托儿问题。问题是prod代码的问题是switch语句非常庞大,并且在某些“案例”中正在对类的属性进行检查,比如如果(myObject.Prop!=null),那么testString=myObject.Stringval。。。在切换结束时,(外部)对testString==null进行了检查,但它保留了上一次迭代的值,因此没有像编码器假设的那样为null,变量在循环中声明。
如果我的问题和示例有点不对劲,我会接到关于日托的电话,因为我正在处理它。我应该提到我比较了循环内外两个变量的IL。那么,人们普遍认为“显然,变量不会在每个循环上重新初始化”?
还有一点信息,变量在每次迭代中都会被初始化,直到有人对ReSharper过于热情,指出“值从未被使用”并将其删除
编辑:
各位,谢谢大家。作为我的第一篇博文,我看到我在未来应该变得更加清晰。我们意外的变量赋值的原因可以归咎于一个没有经验的开发人员,他按照ReSharper告诉他的一切去做,并且在对整个解决方案执行“代码清理”后没有运行任何单元测试。在VSS中查看此模块的历史记录时,我看到了在循环外部声明的变量以及在每次迭代中初始化的变量。有问题的人希望他的重拾器显示“全绿色”,所以“将他的变量移到赋值附近”,然后“删除冗余赋值”!我认为他不会再这样做了……现在就花周末时间运行他错过的所有单元测试吧
如何将问题标记为已回答?您是否收到NullReferenceException错误
从上面的代码中,当您在将变量赋值为null后试图打印变量时,在循环的每个奇数迭代中都会出现该错误 以下是代码的输出:
Loop 0: testDecimal=0 - testString=0
Loop 1: testDecimal= - testString=
Loop 2: testDecimal=6.25 - testString=2
Loop 3: testDecimal= - testString=
Loop 4: testDecimal=12.5 - testString=4
Loop 5: testDecimal= - testString=
Loop 6: testDecimal=18.75 - testString=6
Loop 7: testDecimal= - testString=
Loop 8: testDecimal=25 - testString=8
Loop 9: testDecimal= - testString=
我没有更改您发布的源中的任何内容来生成此输出。请注意,它也不会引发异常
无论如何,您不应该将声明放在for循环中。它会为一次又一次地创建变量占用额外的资源,而您应该做的只是在每次迭代中清除变量
不,没有!应该做与你的建议完全相反的事。但是,即使重置变量更有效,在尽可能小的范围内声明变量也会更清楚。而清晰性(几乎)在任何时候都胜过微观优化。此外,一个变量,一个用法。不要不必要地重用变量
也就是说,变量在这里不会重置或重新初始化——实际上,它们甚至不会被C#初始化!要解决这个问题,只需初始化它们就可以了。这也让我感到惊讶。我原以为在“for”循环中范围会发生变化。情况似乎并非如此。这些值将被保留。编译器似乎足够聪明,可以在第一次输入“for”循环时声明一次变量 我同意之前的文章,你不应该把声明放在“for”循环中。如果您初始化变量,您将占用每个循环上的资源 但是如果您将“for”循环的内部部分分解为一个函数(我知道这仍然很糟糕)。您超出了范围,每次都会创建变量
private void LoopTest()
{
for (int i =0; i< 10; i++)
{
DoWork(i);
}
}
private void Work(int 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 );
}
private void LoopTest()
{
对于(int i=0;i<10;i++)
{
嫁妆(一);
}
}
私人工程(int i)
{
十进制?测试十进制;
字符串测试字符串;
交换机(i%2)
{
案例0:
testDecimal=i/(十进制).32;
testString=i.ToString();
打破
违约:
testDecimal=null;
testString=null;
打破
}
WriteLine(“循环{0}:testDecimal={1}-testString={2}”,i,testDecimal,testString);
}
至少我学到了一些新东西。以及在循环中声明变量到底有多糟糕 摘要 将用于在循环内声明变量的生成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 );
}
}
}
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 );
}
}
}
.method public hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 8
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal> 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<valuetype [mscorlib]System.Decimal>::.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<valuetype [mscorlib]System.Decimal>
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<valuetype [mscorlib]System.Decimal>
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
}
使用系统;
甲级
{
公共静态void Main()
{
对于(int i=0;i<10;i++)
{
十进制?测试十进制;
字符串测试字符串;
交换机(i%2)
{
案例0:
testDecimal=i/(十进制).32;
.method public hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 8
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.Decimal> 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<valuetype [mscorlib]System.Decimal>::.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<valuetype [mscorlib]System.Decimal>
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<valuetype [mscorlib]System.Decimal>
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
}
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();