Introduction
This post does not focus on one particular new feature is the rich new feature set of C# 3.0, but rather studies a set of features, which are loosely related, and play a roll in the enablement of LINQ in the .NET 3.5 framework, which is ultimately the focus of this series of posts.
In this post, we will study the following topics:
- Automatic properties. Automatic properties allow you to declare class properties using a very terse syntax, increasing your productivity and reducing the amount of typing that you have to do.
- Object Initializers. Object initializers allow you to use a focused, "short hand" notation to initialize the properties of a class.
- Collection Initializers. Like object Initializers allow you to use a "short hand" syntax for the initialization of a class, collection initializers allow you to do the same thing for your collections.
Previous posts is this series are:
The code for this feature can be downloaded here.
Ok enough talk, let's have a look at our first "feature of the day": Automatic Properties!
Automatic Properties
Overview
Note: the code for this feature is contained in the "AutomaticProperties" console application of the code.
Everybody has written typical "data container" classes before. These are the typical classes which contains simple fields, and properties with both a getter and a setter for each fields. And every develop I know simply HATES writing these things, because they are what I call "monkey code", code that could be written by semi-intelligent primates (or even a project manager ;-).
Below are some simple examples of such classes:
1 namespace AutomaticProperties.Classic
2 {
3 /// <summary>
4 /// This is our "classic, old style" CoffeeShop class
5 /// </summary>
6 public class CoffeeShop
7 {
8 private string _shopName;
9 private Address _address;
10 private decimal _priceOfLatte;
11 private string _baristaName;
12
13 public string ShopName
14 {
15 get
16 {
17 return _shopName;
18 }
19 set
20 {
21 _shopName = value;
22 }
23 }
24
25 public Address Address
26 {
27 get
28 {
29 return _address;
30 }
31 set
32 {
33 _address = value;
34 }
35 }
36
37 public Decimal PriceOfLatte
38 {
39 get
40 {
41 return _priceOfLatte;
42 }
43 set
44 {
45 _priceOfLatte = value;
46 }
47 }
48
49 public string BaristaName
50 {
51 get
52 {
53 return _baristaName;
54 }
55 set
56 {
57 _baristaName = value;
58 }
59 }
60 } // class AutomaticProperties.Classic.CoffeeShop
61 }
62
1 namespace AutomaticProperties.Classic
2 {
3 /// <summary>
4 /// This is our "classic, old style" Address class
5 /// </summary>
6 public class Address
7 {
8 private string _streetName;
9 private int _streetNumber;
10 private string _city;
11 private string _state;
12 private int _zipCode;
13
14 public string StreetName
15 {
16 get
17 {
18 return _streetName;
19 }
20 set
21 {
22 _streetName = value;
23 }
24 }
25
26 public int StreetNumber
27 {
28 get
29 {
30 return _streetNumber;
31 }
32 set
33 {
34 _streetNumber = value;
35 }
36 }
37
38 public string City
39 {
40 get
41 {
42 return _city;
43 }
44 set
45 {
46 _city = value;
47 }
48 }
49
50 public string State
51 {
52 get
53 {
54 return _state;
55 }
56 set
57 {
58 _state = value;
59 }
60 }
61
62 public int ZipCode
63 {
64 get
65 {
66 return _zipCode;
67 }
68 set
69 {
70 _zipCode = value;
71 }
72 }
73 } // class AutomaticProperties.Classic.Address
74
The code above defines two classes, both of which are in the AutomaticProperties.Classic namespace:
- CoffeeShop
- Address
Automatic properties allow you to avoid having to manually declare a private field and write the get/set logic -- instead the compiler will automatically creating the private field and the default get/set operations for you. Below are the CoffeeShop and Address classes rewritten using Automatic Properties (note that both classes are in the AutomaticProperties.New namespace):
1 public class CoffeeShop
2 {
3 public string ShopName { get; set; }
4 public Address Address { get; set; }
5 public decimal PriceOfLatte { get; set; }
6 public string BaristaName { get; set; }
7 }
1 public class Address
2 {
3 public string StreetName { get; set; }
4 public int StreetNumber { get; set; }
5 public string City { get; set; }
6 public string State { get; set; }
7 public int ZipCode { get; set; }
8 }
That's it. Note that we went from 100+ lines of code to a grand total of 15!
When the C# 3.0 compiler encounters an empty get/set property implementation like we created above in the CoffeeShop and Address classes, it will now automatically generate a private field for us within our class, and implement a public getter and setter property implementation for it. The benefit of this is that from a type-contract perspective, the class looks exactly like it did with my first (much more verbose) implementation shown above. This means that -- unlike resorting to public fields -- I can in the future add validation logic within my property setter implementation without having to change any external component that references my class.
Usage Example
Now, using "classic" classes, and classes that use automatic properties is completely identical. To provide this, I have included a small piece of client code in the download file, that looks as follows:
1 using System;
2
3 // Comment out one of the using statements below
4 // to either use the "Classic" or the "New-Style"
5 // Automatic Properties-style classes
6
7 using AutomaticProperties.Classic; // uses class classes
8 //using AutomaticProperties.New; // uses automatic properties
9
10 namespace AutomaticProperties
11 {
12 class Program
13 {
14 static void Main(string[] args)
15 {
16 Address address = new Address();
17 address.StreetNumber = 50;
18 address.StreetName = "Passover Lane";
19 address.ZipCode = 6703;
20 address.State = "MO";
21 address.City = "Missoula";
22
23 CoffeeShop coffeeShop = new CoffeeShop();
24 coffeeShop.Address = address;
25 coffeeShop.PriceOfLatte = 3.95M;
26 coffeeShop.ShopName = "It's a Grind!";
27 coffeeShop.BaristaName = "6 Shot Lucie";
28 }
29 }
30 }
31
You can comment out either the "using AutomaticProperties.Classic" or the "AutomaticProperties.New" line, and depending on which "using" line is not commented out, the code will use the classic classes, or the classes with the automatic properties. Note though that the rest of code is unchanged.
There is one interesting thing to try though. If you use the "classic" objects, and you set a breakpoint on a line that sets a property, for example:

and you "Step In" (F11), then you will actually go to the setter of the property implementation, as shown below:

this makes sense, since we provided this setter in C#!
Automatic Properties in detail
Now, when you switch to the "automatic properties" version of the objects, and you set the same breakpoint and you step in, you notice that nothing actually happens, which makes sense, because there is only direct IL code, generated by the compiler behind the setter implementation, and no C# code, so the compiler has nowhere to go!
The easiest way to verify this is to bring out our old friend ILDASM.exe, and open our "automatic properties" version Address class is this tool. It would look something like this:

now, we can compare this to the "classic" style Address class:

The we notice that on the surface things very much look the same. Both classes have their properties, and their getters and setters. BUT the big difference is the the "classic" version has the actual fields as we defined them, while for the "automatic properties" version,the compiler injected a private member variable, prefixed with <k>BackingField, like this one for the City property:
1 .field private string '<City>k__BackingField'
2 .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
The CompilerGeneratedAttribute is useful if you have a tool that would like to find out if something is auto-generated. The getter IL implementation look like this:
1 .method public hidebysig specialname instance string
2 get_City() cil managed
3 {
4 .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
5 // Code size 11 (0xb)
6 .maxstack 1
7 .locals init (string V_0)
8 IL_0000: ldarg.0
9 IL_0001: ldfld string AutomaticProperties.New.Address::'<City>k__BackingField'
10 IL_0006: stloc.0
11 IL_0007: br.s IL_0009
12 IL_0009: ldloc.0
13 IL_000a: ret
14 } // end of method Address::get_City
Note the reference to the backing field in line 9. The setter IL looks very similar:
1 .method public hidebysig specialname instance void
2 set_City(string 'value') cil managed
3 {
4 .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
5 // Code size 8 (0x8)
6 .maxstack 8
7 IL_0000: ldarg.0
8 IL_0001: ldarg.1
9 IL_0002: stfld string AutomaticProperties.New.Address::'<City>k__BackingField'
10 IL_0007: ret
11 } // end of method Address::set_City
Again note the setting of the backing field at line 9. So, as you can see, the "automatic property" version of the code has NO C# code behind the getters and setters of the properties, which is why we could not step into the code with the debugger.
One last important note: Automatic properties should have both a getter and a setter. If you leave out one of the two, you will get a compiler error like this:
Error 1 'AutomaticProperties.New.Address.City.get' must declare a body because it is not marked abstract or extern.
Automatically implemented properties must define both get and set accessors.
When you think about it this really makes sense, the whole concept behind automatic properties is to allow you to quickly write typical "data container" or "property bag" classes, which typically have read/write properties.
Object Initializers
Overview
Note: the code for this feature is contained in the "ObjectInitializers" console application of the code.
When we write code that deals with simple objects, we tend to rely heavily on the use of properties. The sample code for this section contains a class named "CarModel" which represents the model of a car. The class is shown below:
1 public class CarModel
2 {
3 private string _make;
4 private string _model;
5 private int _year;
6
7 public string Make
8 {
9 get { return _make; }
10 set { _make = value; }
11 }
12
13 public string Model
14 {
15 get { return _model; }
16 set { _model = value; }
17 }
18
19 public int Year
20 {
21 get { return _year; }
22 set { _year = value; }
23 }
24
25 public override string ToString()
26 {
27 return String.Format(
28 "Make: {0}, Model: {1}, Year: {2} ", Make, Model, Year);
29 }
30 }
31
In a typical approach, we would initialize a new instance of this class as follows:
1 private static void CreateCarModelTraditional()
2 {
3 CarModel carModel = new CarModel();
4 carModel.Make = "Acura";
5 carModel.Model = "TL";
6 carModel.Year = 2007;
7 Console.WriteLine("Traditional CarModel: " + carModel);
8
9 } // method CreateCarModelTraditional
While this is a tried-and-true approach that has served us well for many years, it is very verbose. C# 3.0 now offers a new feature called "Object Initializers", which allows us to write the above code as follows:
1 private static void CreateCarModelWithInitializer()
2 {
3 CarModel carModel = new CarModel {Make = "Acura", Model = "TL", Year = 2007};
4 Console.WriteLine("\nCarModel with Initializer: " + carModel);
5
6 } // method CreateCarModelWithInitializer
All this is is really "Syntactic Sugar", which at the IL level results in the same code as the tradition approach. Indeed, the compiler will automatically generate all of the property-setter code, which has exactly the same implementation as our first, more verbose example (more in on this in our "In-Depth" section).
Note that Object Initializers will also work with more complex objects, which have nested sub-objects. For example, below is the code for a "Car" class, which contains an embedded "CarModel" instance:
1 public class Car
2 {
3 public CarModel CarModel { get; set; }
4 public int EngineCC { get; set; }
5 public int HoursePower { get; set; }
6 public string Color { get; set; }
7
8 public override string ToString()
9 {
10 return String.Format(
11 "Make: {0}, Model: {1}, Year: {2}, \n\tEngineCC: {3}, HorsePower: {4}, Color: {5}",
12 CarModel.Make, CarModel.Model, CarModel.Year, EngineCC, HoursePower, Color);
13 }
14 }
Note that is this case, I used the more compact "Automatic Properties" approach described in the first part of this post . Indeed Object Initializers work directly with the property setters, so they don't know or care that you used the traditional approach, or the "Automatic Properties" approach to create your properties.
Below is an example of an object initializer for a Car instance:
1 private static void createCompoundObject()
2 {
3 // Create a Car instance with an initializer
4 Car car = new Car {
5 Color = "White",
6 HoursePower = 320,
7 EngineCC = 3600,
8 CarModel = new CarModel {Make = "Acura", Model = "TL Type S", Year = 2007}};
9
10 Console.WriteLine("\nComplete Car With Initializer: " + car + Environment.NewLine);
11
12 } // method createCompoundObject
In line 8, you can see how we embedded the initializer for the CarModel directly into the initializer for the Car instance.
Object Initializers in depth
In this section, we will look at the code that is generated by the compiler when we write an object initializer. For example, is we pull out ILDASM (yes, always have that one on your tool belt ;-), and look at the code for the method CreateCarModelWithInitializer(), we see the following:
1 .method private hidebysig static void CreateCarModelWithInitializer() cil managed
2 {
3 // Code size 63 (0x3f)
4 .maxstack 2
5 .locals init ([0] class ObjectInitializers.CarModel carModel,
6 [1] class ObjectInitializers.CarModel '<>g__initLocal0')
7 IL_0000: nop
8 IL_0001: newobj instance void ObjectInitializers.CarModel::.ctor()
9 IL_0006: stloc.1
10 IL_0007: ldloc.1
11 IL_0008: ldstr "Acura"
12 IL_000d: callvirt instance void ObjectInitializers.CarModel::set_Make(string)
13 IL_0012: nop
14 IL_0013: ldloc.1
15 IL_0014: ldstr "TL"
16 IL_0019: callvirt instance void ObjectInitializers.CarModel::set_Model(string)
17 IL_001e: nop
18 IL_001f: ldloc.1
19 IL_0020: ldc.i4 0x7d7
20 IL_0025: callvirt instance void ObjectInitializers.CarModel::set_Year(int32)
21 IL_002a: nop
22 IL_002b: ldloc.1
23 IL_002c: stloc.0
24 IL_002d: ldstr "\nCarModel with Initializer: "
25 IL_0032: ldloc.0
26 IL_0033: call string [mscorlib]System.String::Concat(object,
27 object)
28 IL_0038: call void [mscorlib]System.Console::WriteLine(string)
29 IL_003d: nop
30 IL_003e: ret
31 } // end of method Program::CreateCarModelWithInitializer
32
Now, when we compare this code with the code for the CreateCarModeTraditional():
1 .method private hidebysig static void CreateCarModelTraditional() cil managed
2 {
3 // Code size 61 (0x3d)
4 .maxstack 2
5 .locals init ([0] class ObjectInitializers.CarModel carModel)
6 IL_0000: nop
7 IL_0001: newobj instance void ObjectInitializers.CarModel::.ctor()
8 IL_0006: stloc.0
9 IL_0007: ldloc.0
10 IL_0008: ldstr "Acura"
11 IL_000d: callvirt instance void ObjectInitializers.CarModel::set_Make(string)
12 IL_0012: nop
13 IL_0013: ldloc.0
14 IL_0014: ldstr "TL"
15 IL_0019: callvirt instance void ObjectInitializers.CarModel::set_Model(string)
16 IL_001e: nop
17 IL_001f: ldloc.0
18 IL_0020: ldc.i4 0x7d7
19 IL_0025: callvirt instance void ObjectInitializers.CarModel::set_Year(int32)
20 IL_002a: nop
21 IL_002b: ldstr "Traditional CarModel: "
22 IL_0030: ldloc.0
23 IL_0031: call string [mscorlib]System.String::Concat(object,
24 object)
25 IL_0036: call void [mscorlib]System.Console::WriteLine(string)
26 IL_003b: nop
27 IL_003c: ret
28 } // end of method Program::CreateCarModelTraditional
29
We note the the ONLY difference between the generated IL is the following:
.locals init ([0] class ObjectInitializers.CarModel carModel,
[1] class ObjectInitializers.CarModel '<>g__initLocal0')
The second line in this code block is some bookkeeping for the compiler to remember that Object Initializers were used for this code, but for the rest the code is identical, and the total size of the the general IL is almost the same (61 and 63 bytes), so there is really no overhead in using object initializers.
One case where you need to pay a bit of attention is if you have any object that has a non-default constructor, as show in the example below:
1 public class Engine
2 {
3 public int Horsepower { get; set; }
4 public int CC { get; set; }
5
6 public Engine(int horsePower)
7 {
8 Horsepower = horsePower;
9 }
10 } // class Engine
11
If you would attempt to use an object initializer in this scenario like this, you will run into some problems:
1 private static void objectWithCtorExample()
2 {
3 Engine engine = new Engine { Horsepower = 120, CC = 2600 };
4 }
Indeed, you will get the following compilation error:
Error 1 'ObjectInitializers.Engine' does not contain a constructor that takes '0' arguments
You have two solutions for this:
- Add a default constructor to your class (in all of our previous examples, this default constructor was automatically written for us by the compiler).
- Use a syntax that uses the constructor, and uses an object initializer for the remaining properties, as is show below:
1 private static void objectWithCtorExample()
2 {
3 Engine engine = new Engine(120) { CC = 2600 };
4 }
SO, the above code is a bit of a "hybrid" (to use a popular expression -) between the use of constructors and object initializers.
Collection Initializers
Overview
Note: the code for this feature is contained in the "CollectionInitializers" console application of the code.
The concept of Object Initializers can be expanded to collections with C# 3.0. Using a new feature, called "Collection Initializers" you an use a compact syntax to initialize a collection.
For example, take the following class:
1 public class Dog
2 {
3 public string Breed { get; set; }
4 public string HairColor { get; set; }
5 public int Weight { get; set; }
6 public bool IsFriendly { get; set; }
7
8 public override string ToString()
9 {
10 return String.Format("\tDog: Breed: {0}, HairColor: {1}, Weight : {2}, IsFriendly: {3}",
11 Breed, HairColor, Weight, IsFriendly);
12 }
13 } // class Dog
in a traditional approach, you could create a collection of dogs as follows:
1 private static void createDogListTraditional()
2 {
3 List<Dog> dogs = new List<Dog>();
4 dogs.Add(new Dog {Breed = "Great Dane", HairColor = "Fawn", Weight = 165, IsFriendly = true});
5 dogs.Add(new Dog { Breed = "Mud", HairColor = "DirtyBlack", Weight = 15, IsFriendly = false });
6
7 } // method createDogListTraditional
8
So, here we do what we always do, we create a collection, and then add new instances to the collection. The approach with a collection initializer results in more compact code, as is shown below:
1 private static void createDogListCollectionInitializer()
2 {
3 List<Dog> dogs = new List<Dog>() {
4 new Dog { Breed = "Great Dane", HairColor = "Fawn", Weight = 165, IsFriendly = true },
5 new Dog { Breed = "Mud", HairColor = "DirtyBlack", Weight = 15, IsFriendly = false }
6 };
7
8 } // method createDogListCollectionInitializer
9
So, we no longer need the "dogs.Add", statements, and our code looks more natural and compact.
Collection Initializers in depth
I looked at the generated IL code in ILDASM, and the code sizes for each of the methods was as follows:
- CreateDogListTraditional(): 128 bytes
- CreateDogListCollectionInitializer(): 130 bytes
So again, there is no noticeable overhead in using collection initializers in your code, and I would greatly recommend that you do so to improve the readability of your code.
Conclusion
I hope this article provided you with a good overview of automatic properties, object initializers and collection initializers. These 3 features are used in every LINQ query, and are actually also very useful when you are writing regular production code.
In our next post, we will look at C# 3.0 lambda expressions, which go directly to the core of the how LINQ expressions are generated by the compiler.
Technorati Tags:
C# 3.0,
LINQ