Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it better to declare a variable inside or outside a loop?

Tags:

scope

c#

.net

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.

like image 291
The Mask Avatar asked Dec 16 '11 14:12

The Mask


People also ask

Should you declare variables inside or outside a loop?

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).

Where is the best place to declare variables?

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.

Can you declare a variable inside a while loop?

Yes. you can declare a variable inside any loop(includes do while loop.


2 Answers

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.

like image 24
jason Avatar answered Sep 24 '22 02:09

jason


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.

like image 91
Jon Hanna Avatar answered Sep 25 '22 02:09

Jon Hanna