Monday, December 04, 2006 4:17 AM
bart
C# 3.0 Feature Focus - Part 2 - Object Initializers
Introduction
In this C# 3.0 Feature Focus Week we'll focus on the new language features that will be part of C# 3.0 "Orcas". As you already might know, the major innovation on Orcas is the LINQ (Language Integrated Query) technology that will make data queries a first class citizen of the language (C# 3.0 and VB 9.0). In order to unlock this potential, a set of language features were added to the language that all work in concert to enable LINQ stuff. However, all of those individual features prove to be useful in isolation as well. Later, we'll focus on LINQ itself.
Object Initializers
So, you're tired of having to set a bunch of properties one by one after you've created an object, like this?
using System;
class Oini
{
public static void Main()
{
Customer c = new Customer();
c.Name = "Bart";
c.City = "Ghent";
c.Age = 23;
}
}
Wouldn't it be nice to do this in just one line of code? I agree, it would be even nicer if the Customer class had a constructor that could help us, but you don't have that level of control all the time and maybe you just want to set some properties or you're out of luck with the constructors available. Therefore, C# 3.0 allows you to do this:
using System;
class Oini
{
public static void Main()
{
Customer c = new Customer() { Name = "Bart", City = "Ghent", Age = 23 };
}
}
You can also omit the () of the constructor call. All of this magic assumes you have some Customer type with public property setters and a default constructor. The IL produced looks as follows:
.method public hidebysig static void Main() cil managed
{
.entrypoint
.locals init (class Customer V_0,
class Customer V_1)
IL_0000: nop
IL_0001: nop
IL_0002: newobj instance void Customer::.ctor()
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldstr "Bart"
IL_000e: callvirt instance void Customer::set_Name(string)
IL_0013: nop
IL_0014: ldloc.1
IL_0015: ldstr "Ghent"
IL_001a: callvirt instance void Customer::set_City(string)
IL_001f: nop
IL_0020: ldloc.1
IL_0021: ldc.i4.s 23
IL_0023: callvirt instance void Customer::set_Age(int32)
IL_0028: nop
IL_0029: ldloc.1
IL_002a: nop
IL_002b: stloc.0
IL_002c: ret
} // end of method Oini::Main
In a similar way you can initialize an object of a type that has public fields on it. Assuming the Name, Age and City properties are now replaced by public fields, the IL would look like this (rather predictable):
.method public hidebysig static void Main() cil managed
{
.entrypoint
.locals init (class Customer V_0,
class Customer V_1)
IL_0000: nop
IL_0001: nop
IL_0002: newobj instance void Customer::.ctor()
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldstr "Bart"
IL_000e: stfld string Customer::Name
IL_0013: ldloc.1
IL_0014: ldstr "Ghent"
IL_0019: stfld string Customer::City
IL_001e: ldloc.1
IL_001f: ldc.i4.s 23
IL_0021: stfld int32 Customer::Age
IL_0026: ldloc.1
IL_0027: nop
IL_0028: stloc.0
IL_0029: ret
} // end of method Oini::Main
In case you do have a constructor at hand but it hasn't enough flexibility, one can combine both approaches:
using System;
class Oini
{
public static void Main()
{
Customer c = new Customer("Bart") { City = "Ghent", Age = 23 };
}
}
which produces this piece of IL:
.method public hidebysig static void Main() cil managed
{
.entrypoint
.locals init (class Customer V_0,
class Customer V_1)
IL_0000: nop
IL_0001: nop
IL_0002: ldstr "Bart"
IL_0007: newobj instance void Customer::.ctor(string)
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: ldstr "Ghent"
IL_0013: callvirt instance void Customer::set_City(string)
IL_0018: nop
IL_0019: ldloc.1
IL_001a: ldc.i4.s 23
IL_001c: callvirt instance void Customer::set_Age(int32)
IL_0021: nop
IL_0022: ldloc.1
IL_0023: nop
IL_0024: stloc.0
IL_0025: ret
} // end of method Oini::Main
Another nice thing about this is that you don't have to specify the property's (or field's) name in case the compiler can find it on its own, like this:
using System;
class Oini
{
public static void Main()
{
Customer c1 = new Customer() { Name = "Bart", City = "Ghent", Age = 23 };
Customer c2 = new Customer() { Name = "John", c1.City, c1.Age };
}
} In the case of Customer c2, the compiler observes a City property is used in the object initializer, so it checks whether there's a public property (or field) called City available that can be set. The same happens for the Age property.
Also think about the convenience when doing "nesting" to construct very complex objects:
using System;
class Oini
{
public static void Main()
{
Customer c = new Customer {
Name = "Bart",
Age = 23,
Address = new Address {
Street = "Andersstreet",
Number = 60,
PostalCode = 9000,
City = "Ghent"
}
};
}
}
Without this feature you'd either end up with a temporary variable to hold the Address object or with a bunch of calls like c.Address.* = <something>.
To conclude, I'd say: just use it! A quiz would be too easy in this case :-).
Del.icio.us |
Digg It |
Technorati |
Blinklist |
Furl |
reddit |
DotNetKicks
Filed under: C# 3.0