在局部变量中,打开优化后,编译器will(至少有时)编译为首先分配给变量,然后调用的代码Add
(或为对象初始值设定项设置属性)。
如果您使用静态变量或实例变量,您会看到不同的行为:
class Test
{
static List<int> StaticList = new List<int> { 1 };
List<int> InstanceList = new List<int> { 2 };
}
给出以下类型初始值设定项 IL:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// Code size 21 (0x15)
.maxstack 2
.locals init (class [mscorlib]System.Collections.Generic.List`1<int32> V_0)
IL_0000: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldc.i4.1
IL_0008: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
IL_000d: nop
IL_000e: ldloc.0
IL_000f: stsfld class [mscorlib]System.Collections.Generic.List`1<int32> Test::StaticList
IL_0014: ret
} // end of method Test::.cctor
以及以下构造函数 IL:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 29 (0x1d)
.maxstack 3
.locals init (class [mscorlib]System.Collections.Generic.List`1<int32> V_0)
IL_0000: ldarg.0
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.2
IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
IL_000e: nop
IL_000f: ldloc.0
IL_0010: stfld class [mscorlib]System.Collections.Generic.List`1<int32> Test::InstanceList
IL_0015: ldarg.0
IL_0016: call instance void [mscorlib]System.Object::.ctor()
IL_001b: nop
IL_001c: ret
} // end of method Test::.ctor
在这两种情况下,集合都会被填充before该字段已设置。现在这并不是说可能不再存在内存模型问题,但它是not与设置为引用空集合的字段相同,然后Add
正在拨打电话。从分配线程的角度来看,分配发生在Add
.
一般来说,对象初始值设定项和集合初始值设定项表达式都相当于使用临时变量构造对象 - 因此,在赋值中使用它的情况下,属性设置器都会被调用before分配发生。
然而我不相信any针对对象/集合初始值设定项对其他线程的可见性给出了特殊保证。我建议您想象一下如果按照规范“手写”地写出代码会是什么样子,然后从那里进行推理。
There are为静态初始化程序和构造函数提供的保证 - 但主要是在 .NET 的 Microsoft 实现中,而不是“一般”保证(例如在 C# 规范或 ECMA 规范中)。