Object and Collection Initializers (C# Programming Guide)
Updated: October 2010
Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to explicitly invoke a constructor. The following example shows how to use an object initializer with a named type, Cat. Note the use of auto-implemented properties in the Cat class. For more information, see Auto-Implemented Properties (C# Programming Guide).
Although object initializers can be used in any context, they are especially useful in LINQ query expressions. Query expressions make frequent use of anonymous types, which can only be initialized by using an object initializer, as shown in the following declaration.
var pet = new { Age = 10, Name = "Fluffy" };
Anonymous types enable the select clause in a LINQ query expression to transform objects of the original sequence into objects whose value and shape may differ from the original. This is useful if you want to store only a part of the information from each object in a sequence. In the following example, assume that a product object (p) contains many fields and methods, and that you are only interested in creating a sequence of objects that contain the product name and the unit price.
When this query is executed, the productInfos variable will contain a sequence of objects that can be accessed in a foreach statement as shown in this example:
foreach(var p in productInfos){...}
Each object in the new anonymous type has two public properties which receive the same names as the properties or fields in the original object. You can also rename a field when you are creating an anonymous type; the following example renames the UnitPrice field to Price.
select new {p.ProductName, Price = p.UnitPrice};
Collection initializers let you specify one or more element intializers when you initialize a collection class that implements IEnumerable. The element initializers can be a simple value, an expression or an object initializer. By using a collection initializer you do not have to specify multiple calls to the Add method of the class in your source code; the compiler adds the calls.
The following examples shows two simple collection initializers:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> digits2 = new List<int> { 0 + 1, 12 % 3, MakeInt() };
The following collection initializer uses object initializers to initialize objects of the Cat class defined in a previous example. Note that the individual object initializers are enclosed in braces and separated by commas.
You can specify null as an element in a collection initializer if the collection's Add method allows it.
// The following code consolidates examples from the topic. class ObjInitializers { class Cat { // Auto-implemented properties. public int Age { get; set; } public string Name { get; set; } } static void Main() { Cat cat = new Cat { Age = 10, Name = "Fluffy" }; List<Cat> cats = new List<Cat> { new Cat(){ Name = "Sylvester", Age=8 }, new Cat(){ Name = "Whiskers", Age=2 }, new Cat(){ Name = "Sasha", Age=14 } }; List<Cat> moreCats = new List<Cat> { new Cat(){ Name = "Furrytail", Age=5 }, new Cat(){ Name = "Peaches", Age=4 }, null }; // Display results. System.Console.WriteLine(cat.Name); foreach (Cat c in cats) System.Console.WriteLine(c.Name); foreach (Cat c in moreCats) if (c != null) System.Console.WriteLine(c.Name); else System.Console.WriteLine("List element has null value."); } // Output: //Fluffy //Sylvester //Whiskers //Sasha //Furrytail //Peaches //List element has null value. }
- 12/19/2010
- Drew Noakes
- 6/21/2011
- Greg Bray
Requiring ICollection could have worked, except that doing so would imply the use of the ICollection.Add() method. For types implementing ICollection and ICollection<T>, that either means potentially boxing value types, or complicating the specification to deal with sometimes using ICollection and sometimes using ICollection<T>.
Also, even if one accepts that, going through ICollection or ICollection<T> means using only one Add() method. As things stand today, normal overload resolution occurs on the items being added. So if you do happen to have an IEnumerable type that has overloaded Add() methods that accept a variety of types, you can include any of those types in the initializer and still be sure that the correct Add() method will be used for that type.
I agree that from a general philosophical point of view, keying off of some magic method in a type for the feature seems a little weak, especially for a strongly-static language like C#. But note that this similar to the approach used for LINQ, where the main thing that matters are the presence of the requisite Where(), Select(), OrderBy(), etc. methods so that after the compiler has mechanically translated the expression to plain method calls, all that happens after that is normal compilation that doesn't need to know anything about the "fancy" feature.
Same thing here. All the compiler has to do is do a quick check on the constructed type for the IEnumerable interface, and then mechanically convert the initializer to literal Add() calls, which are then compiled normally without having to treat them differently from user-written code (such as providing a temp variable of the appropriate interface type, and calling the interface's Add() method instead).
So at least this isn't some single anomaly in the language. :)
- 9/16/2010
- peted66616
- 8/19/2010
- SvenL
