简体   繁体   中英

C# 6 Auto Initialization Property and the use of backing fields

Prior to C# 6, the initialization of properties did not use backing fields to initialize default values. In C#6, it uses the backing fields to initialize with new Auto initialization properties .

I'm curious why prior to C#6 IL uses the property definition to initialize. Is there a specific reason for this? or is it not implemented properly before C#6?

Before C# 6.0

public class PropertyInitialization
{
    public string First { get; set; }

    public string Last { get; set; }

    public PropertyInitialization()
    {
      this.First = "Adam";
      this.Last = "Smith";
    }
}

Compiler Generated Code (IL representation)

public class PropertyInitialisation
  {
    [CompilerGenerated]
    private string \u003CFirst\u003Ek__BackingField;
    [CompilerGenerated]
    private string \u003CLast\u003Ek__BackingField;

    public string First
    {
      get
      {
        return this.\u003CFirst\u003Ek__BackingField;
      }
      set
      {
        this.\u003CFirst\u003Ek__BackingField = value;
      }
    }

    public string Last
    {
      get
      {
        return this.\u003CLast\u003Ek__BackingField;
      }
      set
      {
        this.\u003CLast\u003Ek__BackingField = value;
      }
    }

    public PropertyInitialisation()
    {
      base.\u002Ector();
      this.First = "Adam";
      this.Last = "Smith";
    }
  }

C#6

public class AutoPropertyInitialization
{
    public string First { get; set; } = "Adam";
    public string Last { get; set; } = "Smith";
}

Compiler Generated Code (IL representation)

public class AutoPropertyInitialization
  {
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string \u003CFirst\u003Ek__BackingField;
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string \u003CLast\u003Ek__BackingField;

    public string First
    {
      get
      {
        return this.\u003CFirst\u003Ek__BackingField;
      }
      set
      {
        this.\u003CFirst\u003Ek__BackingField = value;
      }
    }

    public string Last
    {
      get
      {
        return this.\u003CLast\u003Ek__BackingField;
      }
      set
      {
        this.\u003CLast\u003Ek__BackingField = value;
      }
    }

    public AutoPropertyInitialization()
    {
      this.\u003CFirst\u003Ek__BackingField = "Adam";
      this.\u003CLast\u003Ek__BackingField = "Smith";
      base.\u002Ector();
    }
  } 

I'm curious why prior to C#6 IL uses the property definition to initialize. Is there a specific reason for this?

Because setting a value through auto-property initialization and setting the value in a constructor are two different things. They have different behaviours.

Recall that properties are accessor methods which wrap around fields. So this line:

this.First = "Adam";

is equivalent to:

this.set_First("Adam");

You can even see this in Visual Studio! Try writing a method with the signature public string set_First(string value) in your class and watch as the compiler complains about you stepping on it's toes.

And just like methods, these can be overridden in child classes. Check out this code:

public class PropertyInitialization
{
    public virtual string First { get; set; }

    public PropertyInitialization()
    {
        this.First = "Adam";
    }
}

public class ZopertyInitalization : PropertyInitialization
{
    public override string First
    {
        get { return base.First; }
        set
        {
            Console.WriteLine($"Child property hit with the value: '{0}'");
            base.First = value;
        }
    }
}

In this example, the line this.First = "Adam" will call the setter in the child class. Because you're calling a method, remember? If the compiler were to interpret this method call as a direct call to the backing field, it wouldn't end up calling the child setter. The act of compiling your code would change the behaviour of your program. Not good!

Auto-properties are different. Lets change the first example by using an auto-property initializer:

public class PropertyInitialization
{
    public virtual string First { get; set; } = "Adam";
}

public class ZopertyInitalization : PropertyInitialization
{
    public override string First
    {
        get { return base.First; }
        set
        {
            Console.WriteLine($"Child property hit with the value: '{0}'");
            base.First = value;
        }
    }
}

With this code, the setter method in the child class will not be called. This is intentional. An auto-property initializer is designed to set the backing field directly. They look and behave like field initializers, which is why we can even use them on properties without setters, like this:

public string First { get; } = "Adam";

There's no setter method here! We would have to directly access the backing field to do this. Auto-properties allow programmers to create immutable values while still being able to benefit from nice syntax.

The only time it makes a difference is if the property setter has more effects than simply setting the value. For auto-implemented properties, the only time that can happen is if they are virtual and overridden. In that case, calling the derived class method before the base class constructor has run is a very bad idea. C# goes through a lot of trouble to make sure you do not accidentally end up with references to not yet fully initialised objects. So it has to set the field directly to prevent that.

Keep in mind that values set as default for properties are not being set in the constructor (your code shows that: assigments, then constructor).

Now, the C# spec says that autoinitialization values are set before the constructor. This makes sense: When these values are set again in the constructor, they are overridden.

Now - before the constructor is called - there are no getter and setter methods initialized. How should they be used?

Thats why the (by then uninitialized backing-fields) are being initialized directly.

As hvd mentioned, there would also be a problem with virtual calls, but that they aren't even initialized is the main reason.


It still behaves the same way as before if you assign values in the constructor:

Example with property that is autoinitialized and changed in the ctor

Why isn't this being optimized out?

See my question about this topic:

But shouldn't it optimize that out?

It probably could, but only if that class doesn't inherit from another class that uses that value in its constructor, it knows that it's an auto-property and the setter doesn't do anything else.

That would be a lot of (dangerous) assumptions. The compiler needs to check a lot of things before making an optimization like that.


Side note:

I assume you use some tool for seeing the compiler generated c# code - it's not entirely accurate. There's no accurate expression for the IL code that is being generated for a constructor - the ctor is not a method in IL, its something different. For the sake of understanding we can assume it is the same tho.

http://tryroslyn.azurewebsites.net/ as example has this comment:

// This is not valid C#, but it represents the IL correctly.

One way you can get the code as shown is that you have your C# 5 code like this:

public class Test : Base
{
    public Test()
    {
        A = "test";
    }

    public string A { get; set; }
}

This will produce (IL) code like this:

public Test..ctor()
{
    Base..ctor();
    A = "test";
}

Your C# 6 code will look like this:

public class Test : Base
{
    public Test()
    {
    }

    public string A { get; set; } = "test";
}

Which produces (IL) code like this:

public Test..ctor()
{
    <A>k__BackingField = "test";
    Base..ctor();
}

Note that if you initialize your property specifically in the constructor, and have a getter/setter property, in C# 6 it will still look like the first piece of code in my answer above, whereas if you have a getter-only field it will look like this:

public Test..ctor()
{
    Base..ctor();
    <A>k__BackingField = "test";
}

So it is quite clear, your C# 5 code looked like the first piece of code above, and your C# 6 code looked like the second piece of code.

So to answer your question: Why does C# 5 and C# 6 behave differently in terms of how it compiles automatic property initialization? The reason is because you cannot do automatic property initialization in C# 5 or prior, and different code compiles differently.

I'm assuming your C# 5.0 code looked like this:

class C
{
    public C()
    {
        First = "Adam";
    }

    public string First { get; private set; }
}

And then in C# 6.0, the only change you made is to make First a get -only autoproperty:

class C
{
    public C()
    {
        First = "Adam";
    }

    public string First { get; }
}

In the C# 5.0 case, First is a property with a setter and your use it in the constructor, so the generated IL reflects that.

In the C# 6.0 version, First does not have a setter, so the constructor has to access the backing field directly.

Both cases make perfect sense to me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM