Freigeben über


Object and Collection Initializers

[Blog Map]  [Table of Contents]  [Next Topic]

Object and collection initializers are a C# 3.0 feature that allows you to create objects and collections of objects in an expression context instead of in a statement context.  Sometimes this is called in-line initialization of objects.

This blog is inactive.
New blog: EricWhite.com/blog

Blog TOCWhen doing FP, we want to create tuples (next topic), and we have to use object and collection initializers in order to do this.

It is very convenient and much more readable to create object collections/graphs/trees using object initializers.

Terms to Understand

There are a few important terms to understand for this discussion. If we define these terms, it will make it much easier to talk about object initialization.

Object Graph

An object graph is a number of C# objects that are tied together in some significant way. For example, you might create a Rectangle object, which contains multiple Point objects:

public class Point
{
private int x, y;
public int X { get { return x; } set { x = value; } }
public int Y { get { return y; } set { y = value; } }
}

public class Rectangle
{
private Point p1 = new Point();
private Point p2 = new Point();
public Point P1 { get { return p1; } }
public Point P2 { get { return p2; } }
}

You would typically create an object graph as follows:

Rectangle r = new Rectangle();
r.P1.X = 1;
r.P1.Y = 2;
r.P2.X = 3;
r.P2.Y = 4;

Statement Context vs. Expression Context

An important idea to understand here is the difference between statement context and expression context.  A statement context is one where the compiler expects and can parse a statement.  An expression context is much more limited.  As an example, you can pass an expression as an argument to a method, but you are not allowed to write a complete statement where an argument is expected.

Creation of Object Hierarchies in an Expression Context

One of the most important characteristics of object initializers is that it allows you to create an entire object hierarchy in an expression context instead of a statement context.

The above object hierarchy could only be initialized in a context where statements are allowed, such as in the body of a method, or maybe when initializing a member variable.

An expression context can be found anywhere in the language where you are allowed to write an expression.  If you need to pass an object hierarchy as an argument to a method, previously, you needed to create the objects, and then pass the root object to the method.  In contrast, with C# 3.0 you can new up the objects in-line as a parameter to the method.

The Simplest Object Initializer

The best way to understand object initializers is to start with the simplest, easiest case and progress from there.

If we have a class Person, as follows:

public class Person
{
string name;
int age;
bool canCode;
public string Name
{
get { return name; }
set { name = value; }
}
public int Age
{
get { return age; }
set { age = value; }
}
public bool CanCode
{
get { return canCode; }
set { canCode = value; }
}
}

Then C# 3.0 allows us to create and initialize an object of type person with the following syntax:

new Person {
Name = "John Doe", Age = 31, CanCode = true
}

This is a pattern that is semantically equivalent to:

Person value = new Person();
value.Name = "John Doe";
value.Age = 31;
value.CanCode = true;

You can use the above object initializer in an expression context.  In the following example, the object is initialized, and then passed as an argument to a method:

PrintPerson(
new Person {
Name = "John Doe", Age = 31, CanCode = true
}
);

Initializing with Constructor Arguments

In the above example, the code called the Person constructor that didn't have arguments. Of course, because no constructors were defined, there was a default constructor created by the compiler without arguments. You could have included the parentheses when calling the constructor, as follows:

new Person() {
Name = "John Doe", Age = 31, CanCode = true
}

Because the code is calling the constructor that takes no arguments, you can eliminate the parentheses.

If there were a constructor defined that took the name as an argument, you could call that constructor in the object initializer, as follows:

new Person("John Doe") {
Age = 31, CanCode = true
}

Initializing Embedded Objects

Sometimes an object contains other objects.  This is a very common pattern.

You can initialize the above Rectangle class with its embedded Point objects as follows:

new Rectangle {
P1 = {
X = 0,
Y = 1
},
P2 = {
X = 2,
Y = 3
}
}

One point to make here - you don't need to use the new operator on the two points because they were created when the Rectangle object was created.

Collection Initializer

Using C# 3.0, you can initialize a collection with elements for the newly created collection.  A collection initializer consists of a sequence of element initializers, enclosed by braces and separated by commas.  Each element initializer specifies an element to be added to the collection object being initialized.

You can initialize a collection of integers like this:

List<int> listInt = new List<int> {
2, 4, 6, 8
};

foreach (int i in listInt)
Console.WriteLine(i);

This outputs:

2
4
6
8

You can also initialize a collection of objects. Using the Point class defined earlier in this topic, you can initialize a collection as follows:

List<Point> listPoints = new List<Point> {
new Point {
X = 1,
Y = 2
},
new Point {
X = 20,
Y = 40
}
};

foreach (Point p in listPoints)
Console.WriteLine("{0}:{1}", p.X, p.Y);

This outputs:

1:2
20:40

[Blog Map]  [Table of Contents]  [Next Topic]

Comments

  • Anonymous
    April 30, 2010
    Surely you should be espousing: var listPoints = new List<Point> {...}; :-)