字典初始化如何在C#中工作?

won*_*rld 22 c# constructor dictionary

var maxDictionary = new Dictionary<int, double> { { 10, 40000 } };
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,编译器是否使用构造函数?或者编译器是否创建了KeyValuePair并添加到字典中?我试图理解编译器如何解释它.

Ser*_*kiy 19

是的,编译器使用默认的无参数构造函数,然后通过Dictionary.Add方法添加集合初始化程序中指定的所有值.正如Jon指出的那样,你的代码被编译成了

Dictionary<int, double> maxDictionary2;
Dictionary<int, double> maxDictionary;

maxDictionary2 = new Dictionary<int, double>();
maxDictionary2.Add(10, 40000.0);
maxDictionary = maxDictionary2;
Run Code Online (Sandbox Code Playgroud)

生成的IL:

.maxstack 3
.locals init (
     [0] class [mscorlib]Dictionary`2<int32, float64> maxDictionary,
     [1] class [mscorlib]Dictionary`2<int32, float64> maxDictionary2)
L_0000: nop 
L_0001: newobj instance void [mscorlib]Dictionary`2<int32, float64>::.ctor()
L_0006: stloc.1 
L_0007: ldloc.1 
L_0008: ldc.i4.s 10
L_000a: ldc.r8 40000
L_0013: callvirt instance void [mscorlib]Dictionary`2<int32, float64>::Add(!0, !1)
L_0018: nop 
L_0019: ldloc.1 
L_001a: stloc.0 
Run Code Online (Sandbox Code Playgroud)

即创建分配给临时变量的字典maxDictionary2,填充值,然后仅将创建和填充字典的引用复制到maxDictionary变量.

请记住,如果您不想使用parammeterless,可以指定任何其他构造函数.例如,你可以使用一个设置初始容量:

var maxDictionary = new Dictionary<int, double>(10) { { 10, 40000 } };
Run Code Online (Sandbox Code Playgroud)

  • 好吧.从逻辑上讲,对`maxDictionary`的赋值来自*Add`调用.如果您实际上重新分配给现有变量,其中集合初始值设定项可以使用现有变量的值,并且它引用*old*集合,则这是可见的. (5认同)
  • @Uriil那是因为DotPeek是"聪明的"并试图给你惯用的C#而不是天真的反编译.就像它会给你一个`foreach`循环而不是直接调用`GetEnumerator()`等. (4认同)
  • @SergeyBerezovskiy:是的 - 赋值运算符的整个右侧在赋值运算符本身之前逻辑执行.这就是*所有*C#的工作方式.在某些情况下,编译器可以优化它 - 我认为它可能会这样做 - 但我喜欢考虑集合初始化器的*logical*操作. (2认同)
  • @SergeyBerezovskiy是的,有,如果你添加一些项目和一个抛出异常你不应该得到一半烘焙字典不是吗? (2认同)
  • @SriramSakthivel这也是一个很好的观察.基本上,在分配之前进行RHS评估可以避免大量不直观的行为/陷阱. (2认同)

Sri*_*vel 7

var maxDictionary = new Dictionary<int, double> { { 10, 40000 } };
Run Code Online (Sandbox Code Playgroud)

这是程序生成的IL

IL_0001:  newobj      System.Collections.Generic.Dictionary<System.Int32,System.Double>..ctor
IL_0006:  stloc.1     // <>g__initLocal0
IL_0007:  ldloc.1     // <>g__initLocal0
IL_0008:  ldc.i4.s    0A 
IL_000A:  ldc.r8      00 00 00 00 00 88 E3 40 
IL_0013:  callvirt    System.Collections.Generic.Dictionary<System.Int32,System.Double>.Add
IL_0018:  nop         
IL_0019:  ldloc.1     // <>g__initLocal0
IL_001A:  stloc.0     // maxDictionary
Run Code Online (Sandbox Code Playgroud)

显然它使用无参数构造函数和调用Add方法.标签"IL_0013"显示对Add方法的调用

等价的c#代码

Dictionary<int, double> maxDictionary;
Dictionary<int, double> temp = new Dictionary<int, double>();
temp.Add(10, 40000.0);
maxDictionary = temp;
Run Code Online (Sandbox Code Playgroud)

值得注意的是编译器使用temp变量,我可以看到两个原因

  1. 确保在遇到异常时不会获得半字形的字典.
  2. 您不希望编译器只读取字段来创建新实例和分配.不是吗?