Is better do:
variable1Type foo; variable2Type baa; foreach(var val in list) { foo = new Foo( ... ); foo.x = FormatValue(val); baa = new Baa(); baa.main = foo; baa.Do(); }
Or:
foreach(var val in list) { variable1Type foo = new Foo( ... ); foo.x = FormatValue(val); variable2Type baa = new Baa(); baa.main = foo; baa.Do(); }
The question is: What is faster 1 case or 2 case? Is the difference is insignificant? Is it the same in real applications? This may be a optimization-micro, but I really want know which is better.
Declaring variables inside or outside of a loop, It's the result of JVM specifications But in the name of best coding practice it is recommended to declare the variable in the smallest possible scope (in this example it is inside the loop, as this is the only place where the variable is used).
The recommended practice is to put the declaration as close as possible to the first place where the variable is used. This also minimizes the scope. From Steve McConnell's "Code Complete" book: Ideally, declare and define each variable close to where it's first used.
Yes. you can declare a variable inside any loop(includes do while loop.
It doesn't matter, it has no effect on performance whatsoever.
but I really want know to do right way.
Most will tell you inside-the-loop makes the most sense.
Performance-wise, let's try concrete examples:
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); } }
I deliberately picked both a value-type and a reference-type in case that affects things. Now, the IL for them:
.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 }
As you can see, apart from the order on the stack the compiler happened to choose - which could just as well have been a different order - it had absolutely no effect. In turn, there really isn't anything that one is giving the jitter to make much use of that the other isn't giving it.
Other than that, there is one sort-of difference.
In my Method1()
, x
and sb
are scoped to the foreach
, and cannot be accessed either deliberately or accidentally outside of it.
In my Method2()
, x
and sb
are not known at compile-time to be reliably assigned a value within the foreach
(the compiler doesn't know the foreach
will perform at least one loop), so use of it is forbidden.
So far, no real difference.
I can however assign and use x
and/or sb
outside of the foreach
. As a rule I would say that this is probably poor scoping most of the time, so I'd favour Method1
, but I might have some sensible reason to want to refer to them (more realistically if they weren't possibly unassigned), in which case I'd go for Method2
.
Still, that's a matter of how the each code can be extended or not, not a difference of the code as written. Really, there's no difference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With