当使用对象初始化,为什么编译器产生一个额外的本地变量?编译器、初始化、变量、对象

2023-09-02 10:24:51 作者:~綄镁の恋づ

虽然关于所以昨天回答一个问题,我注意到,如果一个对象是使用对象初始值初始化,编译器创建一个额外的局部变量。

While answering a question on SO yesterday, I noticed that if an object is initialized using an Object Initializer, the compiler creates an extra local variable.

考虑下面的C#3.0 code,编译发布模式在VS2008:

Consider the following C# 3.0 code, compiled in release mode in VS2008:

public class Class1
{
    public string Foo { get; set; }
}

public class Class2
{
    public string Foo { get; set; }
}

public class TestHarness
{
    static void Main(string[] args)
    {
        Class1 class1 = new Class1();
        class1.Foo = "fooBar";

        Class2 class2 =
            new Class2
            {
                Foo = "fooBar2"
            };

        Console.WriteLine(class1.Foo);
        Console.WriteLine(class2.Foo);
    }
}

使用反射器,我们可以检查code的主要方法:

Using Reflector, we can examine the code for the Main method:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 2
    .locals init (
        [0] class ClassLibrary1.Class1 class1,
        [1] class ClassLibrary1.Class2 class2,
        [2] class ClassLibrary1.Class2 <>g__initLocal0)
    L_0000: newobj instance void ClassLibrary1.Class1::.ctor()
    L_0005: stloc.0 
    L_0006: ldloc.0 
    L_0007: ldstr "fooBar"
    L_000c: callvirt instance void ClassLibrary1.Class1::set_Foo(string)
    L_0011: newobj instance void ClassLibrary1.Class2::.ctor()
    L_0016: stloc.2 
    L_0017: ldloc.2 
    L_0018: ldstr "fooBar2"
    L_001d: callvirt instance void ClassLibrary1.Class2::set_Foo(string)
    L_0022: ldloc.2 
    L_0023: stloc.1 
    L_0024: ldloc.0 
    L_0025: callvirt instance string ClassLibrary1.Class1::get_Foo()
    L_002a: call void [mscorlib]System.Console::WriteLine(string)
    L_002f: ldloc.1 
    L_0030: callvirt instance string ClassLibrary1.Class2::get_Foo()
    L_0035: call void [mscorlib]System.Console::WriteLine(string)
    L_003a: ret 
}

在这里,我们可以看到,编译器产生了两个引用类2 类2 和实例&LT;&GT; g__initLocal0 ),但只有一个参照 1级 1级的实例)。

Here, we can see that the compiler has generated two references to an instance of Class2 (class2 and <>g__initLocal0), but only one reference to an instance of Class1 (class1).

现在,我不是很熟悉,白细胞介素,但它看起来像它的实例&LT;&GT; g__initLocal0 ,设置前类2 =&LT ;&GT;。g__initLocal0

Now, I'm not very familiar with IL, but it looks like it's instantiating <>g__initLocal0, before setting class2 = <>g__initLocal0.

为什么会出现这种情况?

Why does this happen?

它是否遵循那么,使用对象初始化(哪怕是很轻微的)?

Does it follow then, that there is a performance overhead when using Object Initializers (even if it is very slight)?

推荐答案

主题安全和原子性。

首先,考虑这条线code:

First, consider this line of code:

MyObject foo = new MyObject { Name = "foo", Value = 42 };

任何人读这句话可合理假设对象的建设将是原子。在分配之前的对象不存在。一旦任务完成了对象存在并处于预期的状态。

Anybody reading that statement might reasonably assume that the construction of the foo object will be atomic. Before the assignment the object doesn't exist at all. Once the assignment has completed the object exists and is in the expected state.

现在考虑把这一code的两种可能的方式:

Now consider two possible ways of translating that code:

// #1
MyObject foo = new MyObject();
foo.Name = "foo";
foo.Value = 42;

// #2
MyObject temp = new MyObject();  // temp will be a compiler-generated name
temp.Name = "foo";
temp.Value = 42;
MyObject foo = temp;

在第一种情况下的对象实例化在第一行,但不会在预期的状态,直到最后一行已执行完毕。如果另一个线程试图访问该对象的最后一行执行前,会发生什么?该对象将是处于半初始化状态

In the first case the foo object is instantiated on the first line, but it won't be in the expected state until the final line has finished executing. What happens if another thread tries to access the object before the last line has executed? The object will be in a semi-initialised state.

在第二种情况下,对象不存在,直到当它从分配线下决赛温度 。由于参考的分配是一个原子操作这给同样的语义与原始的单行赋值语句。即对象从来没有存在于一个半初始化状态。

In the second case the foo object doesn't exist until the final line when it is assigned from temp. Since reference assignment is an atomic operation this gives exactly the same semantics as the original, single-line assignment statement. ie, The foo object never exists in a semi-initialised state.