Welcome back! This is the second post in my 'Simplifying LINQ' series of weblog posts. If you missed my first post on Lambda Expressions, check it out here. It's not required for this post, so if you're not interested just keep on reading.
In this post I'm going to cover some features that were introduced with the release of the 3.0 version of the .NET framework. These features enable the code patterns that make LINQ so enjoyable. They are: Object Initialization, Type Inference, and Anonymous Types. My goal here is to take the "magic" away from these new concepts by showing how they evolved from concepts that a developer comfortable with .NET 2.0 is already familiar with. Although the syntax has changed, each of these concepts actually compile to code that you already know. I think that the best way to illustrate this is by showing a code sample using the new syntax, then comparing it to the compiled output (reflected with .NET Reflector.)
Object Initialization
Object Initialization allows you to initialize an object's properties when you instantiate it. This is useful for when an overloaded constructor isn't available or when creating projections in a LINQ query.
C# Syntax
MyRectangle objectInitialization
= new MyRectangle() { Height = 2, Width = 4 };
Compiled Output
MyRectangle <>g__initLocal0 = new MyRectangle();
<>g__initLocal0.Height = 2;
<>g__initLocal0.Width = 4;
You should notice that this one line of code actually gets compiled to the three lines of code that you had to write before object initialization. Instantiate your variable, set the Height property, and set the Width property.
Type Inference
With Type Inference, the compiler will infer a variable's type based on the value being assigned to it.
C# Syntax
var myString = "Text"; // Type=String
Compiled Output
string myString = "Text";
In this example, the compiler knew that myString was of type String because a string literal was assigned to it. In the compiled code, "var" is replaced by the actual type "string".
Warning: Don't let yourself fall into the trap that Type Inference sets up. It is NOT a convenience keyword for rapid application development. I know it's tempting to type "var" when your class name is 15+ characters long, but don't do it (at least in production code.) In some situations, this could cause unexpected bugs that wouldn't necessarily be caught by the compiler. The reason for and appropriate use for the var keyword is when the type is not statically know, such as with Anonymous Types.
Anonymous Types
Anonymous types use syntax that instructs the compiler to define a class at compile time. Since the class is created by the compiler at compile time, you as the developer have no way of statically knowing the name of the class because it hasn't been created yet - "var" acts as a placeholder for the type that will be created by the compiler.
C# Syntax
var anonymous = new { FirstName = "Chad", LastName = "Boschert" };
Compiled Output...
<>f__AnonymousType0<<FirstName>j__TPar, <LastName>j__TPar> anonymous
= new <>f__AnonymousType0("Chad", "Boschert");
Notice that var is replaced by the type <>f__AnonymousType0<<FirstName>j__TPar, <LastName>j__TPar>. That class definition is below.
...and new class definition
[DebuggerDisplay(@"\{ FirstName = {FirstName}, LastName = {LastName} }", Type="<Anonymous Type>"), CompilerGenerated]
internal sealed class <>f__AnonymousType0<<FirstName>j__TPar, <LastName>j__TPar>
{
// Fields
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly <FirstName>j__TPar <FirstName>i__Field;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly <LastName>j__TPar <LastName>i__Field;
// Methods
[DebuggerHidden]
public <>f__AnonymousType0(<FirstName>j__TPar FirstName, <LastName>j__TPar LastName);
[DebuggerHidden]
public override bool Equals(object value);
[DebuggerHidden]
public override int GetHashCode();
[DebuggerHidden]
public override string ToString();
// Properties
public <FirstName>j__TPar FirstName { get; }
public <LastName>j__TPar LastName { get; }
}
The compiler-generated class code from .NET Reflector may not be very readable. It basically defines a class with two public string getter properties for FirstName and LastName and a constructor taking two string parameters for FirstName and LastName. The simple syntax of new { Prop = val } does a lot of the footwork that you used to have to do by hand.
Final Thoughts
Hopefully these code examples have illustrated what is actually happening when you use these new language features. Loosely speaking, there is nothing new other than syntax. (Although, I'm sure someone on the language team or compiler team at Microsoft would disagree with me.) We're still just constructing types and setting property values.
Of course the best way to learn something is to play with it. Write some sample code, build it, and look at it in .NET Reflector. These samples require Visual Studio 2008 (Express should work,) and the .NET 3.0 Framework.