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

5

13

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();
    }
  } 
Oysterman answered 3/10, 2016 at 21:27 Comment(7)
Could you show us the C# 5 and/or 6 code which resulted in this IL? Your first sentence confuses me because you couldn't auto initialize property prior to C#6 - you had to do it in a constructor manually.Hereunto
Fileds have to be assigned before the ctor with the autoproperty initialization (spec says so). And before the ctor - there is no property backing field initialized correctly -> this is being done in the ctor -> using properties directly will cause unitialized fileds to be usedAnthurium
You must show the entire piece of code, basically show the entire class both as it was in C# 5 and as it is in C# 6, otherwise we can only guess at the reason why the compiler behaves as it apparently does for you.Sauls
@LasseV.Karlsen thats not needed. Imo its quiet clear what hes talking about. His 'IL Code' is no IL code tho, but C# code that logically represents the IL ;)Anthurium
Yes, but as already commented, you could not initialize properties automatically prior to C# 6, you would either use field initialization or you would put specific code in the constructor to assign values to the properties, the latter of which would produce the first code example. As such, talking about "automatic property initialization prior to C# 6" is rather misleading. I fully understood what he showed of code, but I want him to be specific about what he's asking about.Sauls
@WillRay yes that's what I meant. We could not auto initialize before C#6. But as you said, if you do it in the ctro..manually, the IL shows assigned to the property instead of the backing fields.Oysterman
@WillRay also updated with the C# 5 and 6 code.Oysterman
H
12

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.

Hereunto answered 4/10, 2016 at 12:35 Comment(1)
it seems your answer is more appropriate/complete and explain the exact difference. i.e "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. ". Some of the other answers also correct, but might be bit complicated/not direct, or bit off topic. Thanks!Oysterman
A
2

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.
Anthurium answered 4/10, 2016 at 9:32 Comment(2)
This seem to be the most direct answer to the question and thus IMO should be the accepted answer too.Untouchability
@Mafi yes compiler generated code from JetBrains Dot Peek.Oysterman
S
2

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.

Sauls answered 4/10, 2016 at 10:11 Comment(0)
S
1

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.

Sal answered 3/10, 2016 at 22:15 Comment(2)
There is another point: The values of the initialized properties can be overridden by values set in the constructor. This means the default values have to be set before the constructor (to make sure this does happen). But before the constructor call, the values and methods are not initialized yet (including the setters). Thats why they have to access the fields directly.Anthurium
@Anthurium IIRC, before the base constructor call, all methods are callable already, it's just generally a very bad idea to make use of this: the CIL specification only says that calling instance methods before the base constructor invocation means the code is unverifiable, and unverifiable code can still be correct.Sal
K
0

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.

Kistner answered 3/10, 2016 at 22:10 Comment(3)
I think it's about public string First { get; private set; } = "Adam";, where the setter does exist.Sal
public string First { get; private set; }, and then in the constructor, First = "Adam"; In other words, exactly what you think the C# 5.0 code looks like.Sal
Hes not talking about this. Your claims are correct, but dont really answer the questionAnthurium

© 2022 - 2024 — McMap. All rights reserved.