2

C#6 introduced the ability to initialize properties without a setter, so that one can now use this kind of syntax

public class MyClass
{
    public int Answer { get; } = 42;
}

or even this

public class MyClass
{
    public int Answer { get; }
    public MyClass() 
    {
        Answer = 42;
    }
}

I know (or rather, strongly assume) that this will be be translated into a generated readonly field with an accessor method in CIL, so I understand how this

public class MyClass
{
    public int Answer { get; }
    public MyClass CreateMyClassInstance()
    {
        return new MyClass()
        {
            Answer = 42
        };
    }
}

does not compile (since the assignment technically occurs outside of a constructor, which conflicts with the restrictions imposed by the backing readonly field).

My question is why is such a behavior prohibited in the first place ? Why, from a syntactic and/or compilation point of view, are the property assignments that are part of an object initializer not just considered as extra inline logic to execute after, but still within the object's constructor ? Is it by design, the result of technical limitations or backwards-compatibility, or maybe just a change that's not important enough to be considered ?

JoshVarty
  • 9,066
  • 4
  • 52
  • 80
Phil Gref
  • 987
  • 8
  • 19
  • the c# team has a limited amount of time. there's always trade offs of why certain features are implemented and some left unimplemented. – Daniel A. White Dec 13 '15 at 20:29
  • what would `Answer` be if the object initializer was not specified? – Daniel A. White Dec 13 '15 at 20:30
  • This would break the ability of the constructor to enforce certain conditions if you could reach into the object and modify it after the ctor is done. Also note, that at the CLR level there is no such thing as a property or an object initializer. – usr Dec 13 '15 at 23:37
  • There's no setter for the `Answer` property. What really happens in the initialization of the instance or in the constructor is a direct assignment of the read-only backing filed. See [What's New in C# 6](https://www.simple-talk.com/dotnet/.net-framework/whats-new-in-c-6/ "What's New in C# 6"). – Paulo Morgado Dec 14 '15 at 07:40

2 Answers2

7

Why is such a behavior prohibited in the first place?

Allowing a read-only property to be assigned from an object initializer breaks encapsulation. Any initialization done in constructor could be later overriden by client code in object initializer. It would be impossible for classes to hold their invariants.

Such a feature is not only unnecessary, it's dangerous.

Why, from a syntactic and/or compilation point of view, are the property assignments that are part of an object initializer not just considered as extra inline logic to execute after, but still within the object's constructor?

It would mean that for each and every object initializer, the compiler would have to generate a new constructor. Possibly modifying and breaking a type from another assembly.

Or, the newobj CIL instruction would have to modified to allow execution of some arbitrary code after the constructor.

Jakub Lortz
  • 14,616
  • 3
  • 25
  • 39
  • Good point. I just instinctively considered "one-time" and "readonly" initializations of instance data as the same thing, which in turn made me think that "one-time" assignments from outside the class did not violate the invariants of a class. A good counter example to my own point, as I see know, is that one can decide of the order of the member initialization using an object initializer. – Phil Gref Dec 14 '15 at 05:52
1

Adding on to what Jackub Lortz said, to best way to achieve the effect you're after is by using Answer { get; private set }, or in the C# 6, you can assign a readonly property upon initialization like so:

int Answer { get; } = 42;

Where Answer is readonly from then on. I would assume the value assigned to Answer has to be a constant. If not, you might be able to pull of some hack.

Edit:

You can modify your object like so:

public class MyClass
{
    private bool readOnly;

    private int x;
    public int X
    {
        get { return x; }
        set
        {
            if (readOnly) throw new InvalidOperationException();
            else x = value;
        }
    }

    public MyClass()
    {
        X = 42;
        readOnly = true;
    }
}

This is tested and works. In this code, readOnly can be changed at any point in type, thus allowing you to change X. You can expand on this idea though, where you create this into an object, interface, or generic type, that has a method x.MakeReadOnly();, and does not have anyway to undo that, and any attempt to do so will cause an error.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42