'How is this code able to set a get-only property? [duplicate]

The class JsonSerializerOptions has the following property:

public IList<Serialization.JsonConverter> Converters { get; }

This code works fine, as expected:

using System.Text.Json.Serialization;

var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());

This code fails, as expected:

var options = new JsonSerializerOptions
{
    Converters = new List<JsonConverter> { new JsonStringEnumConverter() }
};

// `Property or indexer 'JsonSerializerOptions.Converters' cannot be assigned to -- it is read only`

So far, so good. But this code also works fine, to my surprise:

var options = new JsonSerializerOptions
{
    Converters = { new JsonStringEnumConverter() }
};

What exactly is happening in that last example? I might be having a mental lapse, but I don't think I've encountered such syntax before. Can someone point out where it is documented? And I don't understand why it's able to circumvent the property's lack of set or init.

Side note: the code I'm asking about was based on this article.



Solution 1:[1]

What exactly is happening in that last example? I might be having a mental lapse, but I don't think I've encountered such syntax before. Can someone point out where it is documented?

It's the collection initializer syntax. It's documented here. And more directly with regards to read-only props here.

Some classes may have collection properties where the property is read-only...You will not be able to use collection initializer syntax discussed so far since the property cannot be assigned a new list. However, new entries can be added nonetheless using the initialization syntax by omitting the list creation.

This syntax works for 'collection types that implement IEnumerable and has Add with the appropriate signature as an instance method or an extension method.'

For deeper reading, take a look at the spec where it says more precisely:

The collection object to which a collection initializer is applied shall be of a type that implements System.Collections.IEnumerable or a compile-time error occurs. For each specified element in order, normal member lookup is applied to find a member named Add. If the result of the member lookup is not a method group, a compile-time error occurs. Otherwise, overload resolution is applied with the expression list of the element initializer as the argument list, and the collection initializer invokes the resulting method. Thus, the collection object shall contain an applicable instance or extension method with the name Add for each element initializer.

Solution 2:[2]

It is a very obscure piece of syntax that exists in C#, but not a lot of people know about it.

The code in the last example is actually equivalent to:

var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());

It does not assign to the Converters property, but intead only inserts the values into the list.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Quacke