Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Type Intializers in C# 3.0


:P
On this page:

During my LINQ to SQL session at DevConnections somebody in the audience asked what happens behind the scenes when you use type initializers in C# 3.0 and well it turns out I gave a hedged answer with a guess that was - uhm - wrong. So here's a discussion of how this feature actually works.

Type Initializers are a new language construct that allow for C# (and VB using similar syntax) to shortcut creating of custom constructors or writing additional code to initialize properties. For example if I have a type like this:

public class TimesheetReportParameters
{
    public DateTime FromDate = DateTime.Now.AddMonths(-1);
    public DateTime ToDate = DateTime.Now;
    public string BillType = "Unbilled";
    public bool MarkAsBilled;
    public bool GenerateXml;
    public bool SummaryReport;
    public string ReportType = "TimeSheetClient";
    public List<int> Companies = new List<int>();
}

I can instantiate and initiaize any number of properties with the following syntax:

TimesheetReportParameters parms =
    new TimesheetReportParameters()
    {
        BillType = "Unbilled",
        GenerateXml = true,
        FromDate = DateTime.Now
    };

This makes it a little less verbose to initialize the type and allows you to quickly and effectively assign default values. Actually it's not all that much less code than for explicit assignment since you'd can use normal assign syntax (ie. parms.FromDate = DateTime.Now;) for it just as easily with only a few more keystrokes. But what's important here is the fact that a single statement/expression can accomplish the task of creating and initializing the new type.

This feature becomes extremely useful when combined with anonymous types and - as is often the case with new C# features when you throw LINQ into the mix. When creating an anonymous type on the fly the above syntax allows you to create the new properties simply by assigning new values to the 'anonymous' properties.

So if I want to create a new anonymous type on the fly I can use code like this:

var workOrder = new
{
    OrderId = "A321",
    Entered = DateTime.Now,
    Title = "Delivery Request",
    Descript = ""
};

In this scenario both a new type is created, with new property assignments and the type initialization features both take advantage of type inferrence in C# 3.0 to determine the types for each of the new properties as well as assigning the value to each property.

All of this becomes extremely useful once you start using LINQ. When you use projection to create your result list the result list type can be either dynamically created using the anonymous type or creating an existing type and using the type initializer syntax to assign the member properties with compact syntax:

IQueryable<CustomerListResult> custList = 
    from c in this.Context.CustomerEntities
    select new CustomerListResult { Company = c.Company, Pk = c.Pk };

The type initializer here allows the expression-like syntax for describing the output type for the result list. The same applies with anonymous type result:

var q  = 
    from c in this.Context.CustomerEntities
    select new { Company = c.Company, Pk = c.Pk };

Here the result list type is projected into an anonymous type but again the type is intialized with the simplified syntax for the value assignments. The syntax is clear and concise and more importantly it allows it to work in a single line of code which is required in order to work inside of a LINQ query.

So during my DevConnections LINQ to SQL session the question was asked: What happens behind the scenes when a type is created with type initializers. I replied that I thought that C# would actually generate a specialized constructor for the type with a parameter signature for each assignment, but I wasn't sure... so after the session I took a look with Reflector to see what actually gets generated by the compiler and - blush - it turns out I was wrong.

The original expression:

TimesheetReportParameters parms =
    new TimesheetReportParameters()
    {
        BillType = "Unbilled",
        GenerateXml = true,
        FromDate = DateTime.Now
    };

Is turned into the following by the compiler:

TimesheetReportParameters g__initLocalb = new TimesheetReportParameters();
g__initLocalb.BillType = "Unbilled";
g__initLocalb.GenerateXml = true;
g__initLocalb.FromDate = DateTime.Now;
TimesheetReportParameters parms = g__initLocalb;

An instance is created and the compiler generates the necessary assign statements. What's surprising though is that an intermediate instance is created for the instance and the assignment which is then assigned to the actual reference returned. Offhand I can't really see what purpose this serves, given that type initializers are always instance level code. Anybody know why this indirect referencing might be required?

It's also interesting to look at the  anonymous type scenario. Looking at the disassembled C# code doesn't help much since Reflector understands anonymous types and so properly displays the C# syntax for the anonymous type looking pretty much the same as the declared code. However looking at the raw IL you can find that the anonymous indeed receives the property assignments as parameters to the constructor:

Here's the C# code again:

var workOrder = new
        {
            OrderId = "A321",
            Entered = DateTime.Now,
            Title = "Delivery Request",
            Descript = ""
        };

The IL:

.method public hidebysig instance void Test() cil managed
{
    .maxstack 5
    .locals init (
        [0] class <>f__AnonymousType0`4<string, valuetype [mscorlib]System.DateTime, string, string> workOrder)
    L_0000: nop 
    L_0001: ldstr "A321"
    L_0006: call valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
    L_000b: ldstr "Delivery Request"
    L_0010: ldstr ""
    L_0015: newobj instance void <>f__AnonymousType0`4<string, valuetype [mscorlib]System.DateTime, string, string>::.ctor(!0, !1, !2, !3)
    L_001a: stloc.0 
    L_001b: ret 
}

The newobj line shows the constructor signature and the variables being loaded into the constructor call. So I was at least partially right <g>...

Looking at the IL code for much of this compiler magic gets you goggle eyed quickly though. Looking at the LINQ query in the above code quickly gets unreadable in IL even for this simple query, not that it matters much.

I know I've started to use a number of the C# 3.0 features quite regularly and when going back to plain 2.x code I already miss them. Type initializers and the related Collection inializers and anonymous types are amongst the ones that I know I'll use frequently.

Posted in CSharp  LINQ  

The Voices of Reason


 

Brock Allen
November 12, 2007

# re: Type Intializers in C# 3.0

"Anybody know why this indirect referencing might be required?"

Multithreading - you don't want the variable to be assigned to the instance before all of the properties are done being assigned.

Travis
November 12, 2007

# re: Type Intializers in C# 3.0

Not a comment for this article, but about your RSS feed. I think you have an error in it. When I view your feed, right after your first article in my feed reader, everything is bolded. It looks like you have a missing bold tag after the first category listed.

For example:
Posted in <b>
        <a href="http://feeds.feedburner.com/WebLog/ShowPosts.aspx?Category=CSharp">
        CSharp</a>&nbsp; <b>
        <a href="http://feeds.feedburner.com/WebLog/ShowPosts.aspx?Category=LINQ">
        LINQ</a>&nbsp; </b>

Luke Breuer
November 12, 2007

# re: Type Intializers in C# 3.0

Multithreading is a good reason; I would also note exceptions. If an exception is thrown inside the property/field-setting initialization block, your object is in an unknown state and it is most likely best to not use it at all -- this is therefore forced by the compiler as another method for creating the so-called "pit of success" that is a design goal for C#.

yaip
November 12, 2007

# re: Type Intializers in C# 3.0

Is there any way to subscribe to comments? Also, if I want to get notified by email if new comments have been added to a particular post, can I do that? It would be so cool if I could monitor comments of a particular post.

Rick Strahl
November 12, 2007

# re: Type Intializers in C# 3.0

@Luke and Brock - yeah, both good reasons. I suppose this behavior gives the behavior that you would see with a constructor - it either works or doesn't and you don't get this in between state.

@Travis - I'm not sure what you mean - the category lists etc. come from the feed and are in raw format. If you see HTML markup errors in the category lists that's something that's happening with your feed reader (or possibly the FeedBurner HTML RSS Feed display if you're viewing the feed as HTML, but I don't see it there).

Travis
November 14, 2007

# re: Type Intializers in C# 3.0

Hi Rick,

When I open your RSS feed in notepad, I can find the error. It is in the description section and in its encoded format contains the following:

Posted in &lt;b&gt;&lt;a href='/WebLog/ShowPosts.aspx?Category=CSharp'&gt;CSharp&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;b&gt;&lt;a href='/WebLog/ShowPosts.aspx?Category=LINQ'&gt;LINQ&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/b&gt;

Rick Strahl
November 15, 2007

# re: Type Intializers in C# 3.0

Thanks Travis. Got it. I was thinking of the wrong category listing <s>... I'll have this fixed later today.

Pete
March 04, 2008

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024