实例化具有初始值的类型是否比两个单独的语句便宜?

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# 中也没用(我以前从未检查过)。