-1

If I have the following property with no setter or backing field

public List<string> TestProperty => new List<string> {"one", "two"};

and try to initialise it in an object initialiser using

TestProperty = new List<string> {"three", "four"}

I get the expected error:

[CS0200] Property or indexer 'X.TestProperty' cannot be assigned to -- it is read only

However if I try to initialise it in an object initialiser using

TestProperty = { "three", "four" }

everything compiles fine but the property still returns new List<string> {"one", "two"};.

@jon-skeet explained in this answer that this second form is a nested object initialiser but I'm still not sure why the above (unhelpfully) compiles and whether this can be stopped and the property removed from IDE auto-complete (ReSharper) in the object initialiser? This can only frustrate users of my code.

Edit: note that I do not want to initialise this property, my concern is that the complier allows something that looks like an initialisation to occur.

MFL
  • 69
  • 1
  • 8
  • 2
    Well, `TestProperty` does return an initialized `List` so you can, safely, add data to it. That the data is not assigned to `TestProperty` is only your decision. – Camilo Terevinto Oct 13 '20 at 20:57
  • 1
    Note that `x.TestProperty == x.TestProperty` is going to give you a surprise. Creation of a new collection should usually be a method not a property. – Ben Voigt Oct 13 '20 at 21:00
  • Adding to the above, having something like in this question is a poor choice for a public API – Camilo Terevinto Oct 13 '20 at 21:01
  • 1
    @CamiloTerevinto "property is initialized" usually mean "there is a value assigned to a property once and it stays that way (till new assignment)", you are using it in less common sense of "property always returns a non-null value"... – Alexei Levenkov Oct 13 '20 at 21:02
  • I fully understand that this property is a computed property with no setter. I'm trying to understand why the syntax is allowed in the object initialiser and what it is doing.... – MFL Oct 13 '20 at 21:04
  • I explained that in my first comment: you're adding that to a list, and that list is subsequently discarded (you cannot get it) – Camilo Terevinto Oct 13 '20 at 21:05
  • Thanks for reply @CamiloTerevinto, can you explain why this is valid syntax for adding? – MFL Oct 13 '20 at 21:05
  • If my mind doesn't fail me at these hours, the code would compile to more or less: `var x = new X(); var list = x.TestProperty; list.Add("three"); list.Add("four"); /* when calling TestProperty again*/ var newList = x.TestProperty;` – Camilo Terevinto Oct 13 '20 at 21:07
  • Thanks @CamiloTerevinto and madreflection. The situation is clear now. Appreciate your help! – MFL Oct 13 '20 at 21:13

1 Answers1

2

Let's say you have this:

public class X 
{
    public List<string> TestProperty => new List<string> {"one", "two"};
}

and then:

public static void Main()
{
    var x = new X 
    {
        TestProperty = { "a", "b" }
    };
}

that would roughly compile to:

var x = new X();
var list = x.TestProperty; // remember, this is a NEW list
list.Add("a");
list.Add("b");

So, when you print (or look at in the Debugger) x.TestProperty, that is an entirely new list, so the values you added were effectively ignored.

Why is that syntax valid? Because the feature was designed so that you would normally have an empty list generated in the constructor and the Add calls would add to that list. It's only your decision in this case to discard those values, as you could have used a backing field instead of returning a new instance.


@madreflection correctly pointed out through this SharpLab that each call to Add would be done after calling x.TestProperty, so "a" and "b" would end up in 2 different lists.

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
  • I will accept this but it might be helpful to add that this syntax "adds" to the (in this case computed) value rather than "sets" it. Hence the absence of a setter is not an issue. – MFL Oct 13 '20 at 21:20
  • Thanks people for your help, appreciated! – MFL Oct 13 '20 at 21:20