Search code examples
c#accessorobject-initializerscollection-initializer

Object & Collection Initializers and Accessors


I'd like to start off with that I'm new to C# so accessors and Object Initializers are a completely new concept to me. That said, I think I have an okay handle on them except the following example is really confusing me:

using System;
using System.Collections.ObjectModel;

class How {
    public ObservableCollection<int> Coll {
        get { return coll_; }
        set {
            Console.WriteLine("Setter for Coll Called!");
            coll_.Clear();
            foreach (int i in value)
                coll_.Add(i);
        }
    }

    public string Field {
        get { return field_; }
        set {
            Console.WriteLine("Setter for field called");
            field_ = value;
        }
    }

    // To confirm the internal coll_ is actually set
    public void Test() {
        foreach(int i in coll_)
            Console.Write(i + " ");
    }

    public How() {
        coll_ = new ObservableCollection<int>();
        field_ = "";
    }

    private ObservableCollection<int> coll_;
    private string field_;
}

public class Test {
    public static void Main() {
        var how = new How {
            Coll = { 1, 2, 3, 4, 5 },
            Field = "Test Field",
        };

        Console.Write("Coll: ");
        foreach (int i in how.Coll)
            Console.Write(i + " ");
        Console.WriteLine();

        Console.WriteLine("Field: " + how.Field);


        Console.Write("Internal coll_: ");
        how.Test();
        Console.WriteLine();
    }
}

The output of the above code is (see live example here):

Setter for field called
Coll: 1 2 3 4 5 
Field: Test Field
Internal coll_: 1 2 3 4 5 

Field operates exactly as I'd expect it to, but Coll confuses me. The setter to Coll is never called, which to me implies that Collection Initializers don't mix will with Properties (or at least non Auto-Properties). However, if that's the case, I would have expected a compile-time error.

Regardless of that part of the behavior, what confuses me even further is that the internal value of coll_ is somehow set to the initializer value.

I'm interested in knowing a) why Coll's set is not called, and also how C# is able to set the value of coll_. Is the use of that identifier in the get and set accessors for Coll enough for C# to identify coll_ as the internal storage; or maybe since it's set because it's the only member of the corresponding type?


Solution

  • var how = new How 
              {
                  Coll = { 1, 2, 3, 4, 5 },
                  Field = "Test Field",
              };
    

    this is a object initialization syntax for How class.

    Coll = { 1, 2, 3, 4, 5 } is a form of collection-itializer syntax meant for collection properties which don't have public setter (but works equally well with those which have setter). This form requieres for Coll to be instantiated (be not null): try to comment coll_ = new ObservableCollection<int>(); line in constructor and the program will crash with NullReferenceException.

    Coll = { 1, 2, 3, 4, 5 } is translated in repeated Coll.Add calls:

    Coll.Add(1);
    Coll.Add(2);
    Coll.Add(3);
    Coll.Add(4);
    Coll.Add(5);
    

    to confirm it add event handler in How constructor:

    public How() 
    {
        coll_ = new ObservableCollection<int>();
    
        coll_.CollectionChanged += (o,e) => 
        { Console.WriteLine("New items: {0}", String.Join (",", e.NewItems.OfType<int>())); };
    
        field_ = "";
    }
    

    Collection initializers are described in §7.6.10.3 of C# Language Specification