实例化具有初始值的类型是否比两个单独的语句便宜?
Is instantiating a type with initial values cheaper than two separate statements?
如果我这样做:
string x = "Whatever";
将它分成两行是否有任何不同(除了明显的语法)?
string x;
x = "Whatever";
第一种方法是纯糖吗?
我问是因为我有一个循环:
foreach(string s in MyListOfStrings)
{
string x = Method(s);
}
想知道这是否更好/更快/更清洁:
string x;
foreach(string s in MyListOfStrings)
{
x = Method(s);
}
我敢肯定这已经被问过并仔细研究过,所以如果有人能给我指出一篇好的文章/文档,我将不胜感激。
写作:
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;
}
此 IL 发布代码 (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
如我们所见,完全没有区别:代码完全相同,不考虑 NOP(内存对齐指令优化 CPU 执行指令的名称我忘记了) .
首先,我很好。
但令我失望的是,对于 C++ 中的循环优化,我在 25 年前就学会了这样做,即使启用了编译器优化,这在 C# 中也没用(我以前从未检查过)。
如果我这样做:
string x = "Whatever";
将它分成两行是否有任何不同(除了明显的语法)?
string x;
x = "Whatever";
第一种方法是纯糖吗?
我问是因为我有一个循环:
foreach(string s in MyListOfStrings)
{
string x = Method(s);
}
想知道这是否更好/更快/更清洁:
string x;
foreach(string s in MyListOfStrings)
{
x = Method(s);
}
我敢肯定这已经被问过并仔细研究过,所以如果有人能给我指出一篇好的文章/文档,我将不胜感激。
写作:
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;
}
此 IL 发布代码 (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
如我们所见,完全没有区别:代码完全相同,不考虑 NOP(内存对齐指令优化 CPU 执行指令的名称我忘记了) .
首先,我很好。
但令我失望的是,对于 C++ 中的循环优化,我在 25 年前就学会了这样做,即使启用了编译器优化,这在 C# 中也没用(我以前从未检查过)。