0

I wrote a class property with only get accessor, like:

public int MyProp { get; }

I cannot assign any value to MyProp, not even privately, via method inside my class, i.e. it's not like if I had a private set. However, somehow, I can set a value to myProp using a constructor like:

public MyClass (int myProp) { this.MyProp = myProp; }

Is the constructor always immune to property accessor specifications? Are they valid only after the constructor runs?

  • You can refer these links : https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly & https://www.pluralsight.com/guides/declare-and-initialize-read-only-auto-properties-in-c to know about read only property with all context. – Pooja K Bhatt Jul 05 '20 at 08:19
  • 1
    Just think if constructor is not allowed to initialise these values then what is the use of these values? It will always be the datatype default value like for int it will be 0. By using only get means we will not allow to change the value once it's initialised – Vivek Nuna Jul 05 '20 at 08:37
  • Yes, constructors can always set properties without a getter. I saw that C# 9 has now an `init` keyword: https://stackoverflow.com/questions/62372422/what-is-difference-between-init-only-and-readonly-in-c-sharp-9 –  Jul 05 '20 at 09:57
  • It is the compiler that is enforcing these rules, so if the compiler has rules to allow code to write to readonly fields, the compiler can do that. Constructors are covered by such rules, as are the new `init` members coming in C# 9. – Lasse V. Karlsen Jul 05 '20 at 10:02

2 Answers2

1

When you create an auto-property the compiler automatically generates backing fields to support it. In the case of a readonly (get only) property this is still the case. Being able to assign to a get-only property in the constructor is simply syntactic sugar supported by the compiler. When you assign to the property, the compiler translates the code so that it assigns to the backing field instead.

For example, assume your constructor:

public MyClass(int myProp)
{ 
    this.MyProp = myProp;
}

This is translated by the compiler to something akin to the following:

public MyClass(int myProp)
{ 
    this._myPropBackingField = myProp;
}

The fact that you have no set accessor then prevents you from assigning to the property everywhere else.

In reality, the backing field recieves an "unspeakable" name that is illegal c# (but valid IL) thus preventing you from attempting to use the field directly. Your example actually looks like the following after the compiler has done its job:

public class MyClass
{
    [CompilerGenerated]
    private readonly int <MyProp>k__BackingField;

    public int MyProp
    {
        [CompilerGenerated]
        get
        {
            return this.<MyProp>k__BackingField;
        }
    }

    public MyClass(int myProp)
    {
        this.<MyProp>k__BackingField = myProp;
    }
}

Note that the constructor is actually assigning to the backing field <MyProp>k__BackingField directly rather than indirectly via the property's setter (since after all one does not exist).

See this SharpLab example.

pinkfloydx33
  • 11,863
  • 3
  • 46
  • 63
1

Just think if the constructor is not allowed to initialize these values then what is the use of these values? It will always be the datatype default value like for int it will be 0. By using only get means we will not allow to change the value once it's initialized.

You can use readonly also, and it is thread-safe by default. but when you use a private set you have to handle the thread-safety explicitly.

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197