Why can I use a collection initializer with private set access from another class?

Consider the following code:

public sealed class Order
{
    public Order()
    {
        Items = new List<OrderItem>();
    }

    public List<OrderItem> Items { get; private set; }
}

public sealed class OrderItem
{
}

and here's Order initialization in another class.

var order = new Order
{
    Items =
    {
        new OrderItem(),
        new OrderItem()
    }
};

Could you explain why it works? As you see the Order has private set property, so I thought it would be impossible to set its value.


Your statement works because the collection initialization syntax uses the Add() method to add the items to the collection rather than setting the member to a new instance of a collection. Essentially, your code is the equivalent of:

var order = new Order();
order.Items.Add(new OrderItem());
order.Items.Add(new OrderItem());

Which is fine since you only ever use the getter method.


Short answer:

It works thru collection initializer which calls Add to add items

Long answer:

Accodingly C# 3.0 cpesification, object which implement IEnumerable and has appropiate Add method can be initialised thru the Add method.

Items has public get accessor and Items it's a List<T> which implements IEnumerable and has Add. Here's how the compiler sees your code

var order = new Order();
order.Items.Add(new OrderItem());
order.Items.Add(new OrderItem());

Please note, the compiler doesn't use info that the List implements IEnumerable, here's the proof, no exception will be thrown

public sealed class Order
{
    public Order()
    {
        Items = new MyCollection();
    }

    public MyCollection Items { get; private set; }
}

public sealed class OrderItem
{
}

public class MyCollection : IEnumerable
{
    private readonly List<OrderItem> _items = new List<OrderItem>();

    public void Add(OrderItem item)
    {
        _items.Add(item);
    }

    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }
}

from C# Language Specification

The collection object to which a collection initializer is applied must be of a type that implements System.Collections.IEnumerable or a compile-time error occurs. For each specified element in order, the collection initializer invokes an Add method on the target object with the expression list of the element initializer as argument list, applying normal overload resolution for each invocation. Thus, the collection object must contain an applicable Add method for each element initializer.