If I do:
string x = "Whatever";
Is that different in ANY way (other than the obvious syntax) to having it split in two lines?
string x;
x = "Whatever";
Is the first way just pure sugar?
I ask because I had a loop:
foreach(string s in MyListOfStrings)
{
string x = Method(s);
}
And wondered if and why this was better / faster / cleaner:
string x;
foreach(string s in MyListOfStrings)
{
x = Method(s);
}
I'm sure this has already been asked and pored over so if anyone can just point me to a good article / doc that'd be appreciated.
Writing that:
static void Test()
{
// Simple initialization
string s1 = "Whatever";
string s2;
s2 = "Whatever";
// Loop optimization
var list = new List<string>();
foreach ( string s in list )
{
string s3 = Method(s);
}
string s4;
foreach ( string s in list )
{
s4 = Method(s);
}
}
static string Method(string s)
{
return s + s;
}
Results in this IL release code (ILSPy):
.method private hidebysig static
void Test () cil managed
{
// Method begins at RVA 0x34f4
// Code size 133 (0x85)
.maxstack 1
.locals init (
[0] string s1,
[1] string s2,
[2] class [mscorlib]System.Collections.Generic.List`1<string> list,
[3] string s4,
[4] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>,
[5] string s,
[6] string s3,
[7] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>,
[8] string s
)
// (no C# code)
IL_0000: nop
// string text1 = "Whatever";
IL_0001: ldstr "Whatever"
IL_0006: stloc.0
// string text2 = "Whatever";
IL_0007: ldstr "Whatever"
IL_000c: stloc.1
// List<string> list = new List<string>();
IL_000d: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
IL_0012: stloc.2
// (no C# code)
IL_0013: nop
// foreach (string item in list)
IL_0014: ldloc.2
IL_0015: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
// (no C# code)
IL_001a: stloc.s 4
.try
{
IL_001c: br.s IL_0032
// loop start (head: IL_0032)
// foreach (string item in list)
IL_001e: ldloca.s 4
IL_0020: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::get_Current()
// (no C# code)
IL_0025: stloc.s 5
IL_0027: nop
// string text3 = Method(item);
IL_0028: ldloc.s 5
IL_002a: call string ConsoleApp.Program::Method(string)
IL_002f: stloc.s 6
// (no C# code)
IL_0031: nop
// foreach (string item in list)
IL_0032: ldloca.s 4
IL_0034: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::MoveNext()
// (no C# code)
IL_0039: brtrue.s IL_001e
// end loop
IL_003b: leave.s IL_004c
} // end .try
finally
{
IL_003d: ldloca.s 4
IL_003f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
IL_0045: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_004a: nop
IL_004b: endfinally
} // end handler
IL_004c: nop
// foreach (string item2 in list)
IL_004d: ldloc.2
IL_004e: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
// (no C# code)
IL_0053: stloc.s 7
.try
{
IL_0055: br.s IL_006a
// loop start (head: IL_006a)
// foreach (string item2 in list)
IL_0057: ldloca.s 7
IL_0059: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::get_Current()
// (no C# code)
IL_005e: stloc.s 8
IL_0060: nop
// string text4 = Method(item2);
IL_0061: ldloc.s 8
IL_0063: call string ConsoleApp.Program::Method(string)
IL_0068: stloc.3
// (no C# code)
IL_0069: nop
// foreach (string item2 in list)
IL_006a: ldloca.s 7
IL_006c: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::MoveNext()
// (no C# code)
IL_0071: brtrue.s IL_0057
// end loop
// }
IL_0073: leave.s IL_0084
} // end .try
finally
{
// (no C# code)
IL_0075: ldloca.s 7
IL_0077: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
IL_007d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0082: nop
IL_0083: endfinally
} // end handler
IL_0084: ret
} // end of method Program::Test
As we can see, there is no difference at all : the code is exactly the same, without considering the NOPs (memory alignment instruction to optimize the CPU thing whose name I forgot that executes the instructions).
For the first thing, I'm ok.
But where I'm disapointed is that for the loop optimization as in C++ I had learned 25 years ago to do that even with compiler optimizations on, that is useless in C# (I had never checked this before).