Assign read-only auto-properties in regular constructors

Topics: C# Language Design
Apr 7, 2014 at 1:12 AM
Jon Skeet actually brought this up in a post, and I didn't see it mentioned here so I figured I would mention it.

Basically primary constructors and read-only auto-properties are VERY closely tied together, in that read-only auto-properties become semi-useless without primary constructors. While I personally think auto-constructors have their place in quick data class/struct creation, they are very limited, and some developers will be very put-off by them. (I've heard several developers already comment that it feels out of place).

In order to make read-only auto-properties work outside of primary constructors, Jon proposes letting them be assigned within constructors, just as readonly fields can be.

This would open up read-only auto-properties to a lot more use-cases, and allow the full functionality of existing constructors to use them.

The only problem I see right now is with the ambiguities of what to do with read-only properties that aren't auto-generated. Obviously it'd be meaningless for these to be assignable, so somehow the compiler would have to distinguish. I don't imagine this would be too confusing for programmers, but there may be some who have complaints about the inconsistencies.

Another option is to expose some sort of new visibility rule for auto-properties that says it can be modified within the constructor, but that's it. Perhaps even the readonly keyword could be re-used here, for instance:
public string Foo { get; readonly set; }

public Bar(string foo)
{
    Foo = foo;
}
I'm personally not a fan of this syntax, perhaps there's a better, or perhaps just exposing any read-only auto-property as settable in the constructor is better.
Apr 7, 2014 at 1:49 PM
Just some more on this.

I think the best syntax is just
public string Foo { get; }

public Bar(string foo)
{
    Foo = foo;
}
And under the hood Foo's private field is a readonly field, and the constructor's references to Foo need to be translated to the private readonly field instead. It should be a fairly simple and small change, but will open up a lot of use cases.
Apr 7, 2014 at 2:25 PM
I don't have a definitive opinion on the syntax or any idea of the challenged involved but I think the objections are perfectly valid. Right now I write a lot of public get private set properties to create a get-only auto property and assign them in constructors. My real intention is immutability but I can't really state it in code with auto properties. Primary constructors are fine but sometimes I need constructor overloads that do something.
Apr 7, 2014 at 2:29 PM
This also shouldn't be seen as an alternative to primary constructors. It could co-exist with primary constructors, and in fact I imagine a lot of the same work would need to be done in regards to setting read-only auto-properties anyways.

This request is really just to allow the read-only auto-properties be assigned in constructors that aren't the primary constructor, very little semantic difference.
Apr 7, 2014 at 5:46 PM
Consistency is a good thing. Your second example is weird: I can't assign a readonly non-auto property.
public string Foo { get { return _foo; } }

public Bar(string foo)
{
  Foo = foo;  // Doesn't compile!
}
If someone wants to initialize an auto-property from the constructor, your first proposal makes sense in my opinion. For someone that already knows readonly in C# 5, it intuitively does what you might think.
Apr 7, 2014 at 6:04 PM
For me the ability to set them in constructor is a must. Otherwise I don't see much use for them in majority of the code I normally write.

As for the syntax the "readonly set" would be good enough, even though it looks a bit like oxymoron. Actually the CLR naming would fit perfectly here as "initonly set" seems just right to me, but C# choose the other keyword and we must live with that. Of course it would be useful to keep the option to initialize them in place as well but in proposed form they look just too limiting for me.
Apr 7, 2014 at 9:35 PM
Edited Apr 7, 2014 at 9:35 PM
Readonly properties can be initialized with the parameters of the primary constructor.
public class Point(int x, int y)
{
    public int X { get; } = x;
    public int Y { get; } = y;

    public Point() : this(0, 0)
    {
    }
}
Apr 8, 2014 at 3:51 AM
Edited Apr 8, 2014 at 3:52 AM
Consistency is a good thing. Your second example is weird: I can't assign a readonly non-auto property.
It's just a matter of where the consistency exists. For me having a readonly set method seems like a complete oxymoron and is inconsistent with how you'd expect either to function. Plus the fact that no set method would ever be generated, and I'd argue that it's just as bad, while being more verbose.

as for the non being able to assign a readonly non-auto property, this inconsitency already exists with primary constructors. This feature is pretty much just to move that equals sign down into the constructor body where arbitrary code can be used, and where most developers would expect to see construction logic.


I also don't think:
public class Point(int x, int y)
{
    public int X { get; } = x;
    public int Y { get; } = y;

    public Point() : this(0, 0)
    {
    }
}
Is a very nice solution to the problem. I'd much prefer to see something like:
public class Point
{
    public int X { get; } = 0;
    public int Y { get; } = 0;

    public Point() {}
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}
Which would be more familiar to existing developers, and would be fairly easy to understand what's happening, even if seeing the { get; } would make them question why you are able to assign to it.

I should note however that this is completely my opinion, and I think both ways of writing should be fully supported, and both will have their places.
Apr 10, 2014 at 2:37 AM
I cannot agree with this thread more, read only autos should be settable in the same scope as a read only field!
Coordinator
Apr 14, 2014 at 11:35 PM
It makes sense to allow assignment to a getter-only auto-property from a constructor. There's a bit of a snag, because assigning to a get/set auto-property today executes the setter, instead of assigning directly to the underlying field. This is observable if the property is virtual (which is a bad idea for auto-properties!)

We would have to either introduce an inconsistency between get-only and get-set auto-properties in what assignment from constructor means, or a breaking change on get/set auto-properties to assign to the underlying field. Neither is great, but probably better than not allowing the assignment to the getter-only auto-property at all.

We'll look at it more.
Apr 15, 2014 at 12:22 AM
Actually I think it makes much more sense for such an assignment to skip the setter. Then the field could also properly be marked as readonly as well.
public class Point(int x, int y)
{
    public int X { get; } = x;
    public int Y { get; } = y;
}
// is equivalent to:
public class Point
{
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; }
    public int Y { get; }
}
// is equivalent to:
public class Point
{
    private readonly int _x;
    private readonly int _y;

    public Point(int x, int y)
    {
        _x = x;
        _y = y;
    }

    public int X
    {
        get { return _x; }
    }

    public int Y
    {
        get { return _y; }
    }
}
The only disadvantage to this would be that a virtual getter-only auto-property could not have a setter overridden by a derived class, but I would argue that this is proper behavior. Having it call the setter would require that the field be read/write in which case you already have the private modifier for the setter which would accomplish the same behavior.

I don't particularly care for primary constructors. I don't like that I have to buy into primary constructors in order to get readonly auto-properties which seems like a completely different problem.
Apr 15, 2014 at 1:01 PM
Edited Apr 15, 2014 at 1:17 PM
What might work is to introduce a syntax like:
int X {get; readonly set;}
Which would compile to the same as Halo_Four mentioned. Then just make the readonly set be optional, and implied if no set is given.
Apr 15, 2014 at 1:49 PM
I don't know, my issue with that is what does the following mean?
public virtual int X { get; readonly set; }
If a derived class then overrides that property would they have a proper setter or not? If so then the field could not be marked as readonly anyway. If not then to have defined a setter, even as readonly may be confusing since the compiler would never be calling a setter.

I think that the current proposed method of defining the auto-property is appropriate. Obviously a getter-only auto-property is of very little use if nobody can assign it so it makes sense that it be at least writable by the class itself. I just think where it is writable needs to be expanded upon and not limited to primary constructors.

For the sake of discussion, what I would propose would be that getter-only auto-properties are always writable by the defining class. All write operations are translated into the appropriate stfld opcode against the generated backing field. This would be permitted anywhere:
public class Foo
{
    public int Bar { get; }

    public Foo()
    {
        Bar = 5;
    }

    public void DoStuff()
    {
        Bar = 10;
    }
}
In addition, the getter-only auto-property could be decorated with the readonly keyword in order to make the backing field readonly, in which case the compiler enforces that write operations are only permitted in any one of the constructors of the class.
public class Foo
{
    public readonly int Bar { get; }

    public Foo()
    {
        Bar = 5;
    }

    public void DoStuff()
    {
        Bar = 10; // compiler error
    }
}
For primary constructors the same rules all apply excepting that now the properties can be set directly via initializer syntax:

public class Foo(int bar, int baz)
{
    public int Bar { get; } = bar;
    public readonly int Baz { get; } = baz;

    public Foo(int bar)
    {
        Bar = bar; // fine
    }

    public void DoStuff()
    {
        Bar = 10; // fine
        Baz = 15; // compiler error
    }
}
This would be translated into the following;
public class Foo
{
    private int _bar;
    private readonly int _baz;

    public int Bar
    {
        get { return _bar; }
    }

    public int Baz
    {
        get { return _baz; }
    }

    public Foo(int bar)
    {
        _bar = bar; // fine
    }

    public Foo(int bar, int baz)
    {
        _bar = bar;
        _baz = baz;
    }

    public void DoStuff()
    {
        _bar = 10; // fine
        _baz = 15; // compiler error
    }
}
Apr 15, 2014 at 2:34 PM
I would propose would be that getter-only auto-properties are always writable by the defining class
So you'd like
public int {get;}
to default to:
public int{get;private set;}
This would make getter only auto-properties essentially useless, since I already can do the second one. I really think that public int{get;} should default to readonly.

Allowing it to be assigned in the constructor should just be a special case, like how you can assign it with a Primary Constructor. Yes it means it doesn't align perfectly with getter-setter auto-properties (which would call the set method), but I think it's intuitive enough that the special case makes it fine. If I say there's a set method, I'd expect the set method to be called. If I don't have a set method, I'd like to still create the initial value within the constructor, just like a readonly field.
Apr 15, 2014 at 3:41 PM
You're right, there really is no practical difference between what I proposed as a writable getter-only auto-property and a normal auto-property with a private setter. The setter method is generated but it is not virtual and is more than likely optimized away at runtime.

That said, I only proposed the concept for the sake of argument. I'm fine with getter-only auto-properties being writable only within a constructor but I think that an explicit readonly modifier not only makes it more clear as to the intent but also makes it more consistent with the syntax for read only fields. If the getter-only auto-properties were only ever writable in a constructor would the backing field be readonly?

As long as the getter-only auto-property is writable by initializer, primary constructor and normal constructor then I'm happy.
Apr 15, 2014 at 5:03 PM
If the getter-only auto-properties were only ever writable in a constructor would the backing field be readonly?
Ideally yes, because readonly has the same semantics we desire here.
As long as the getter-only auto-property is writable by initializer, primary constructor and normal constructor then I'm happy.
I'd add a clarification that they are only writeable by the initializer, primary constructor and normal constructor, but other than that I agree full-heartedly.