in

Foo Theory

Partners in Community - serving up some ice cold Kool-Aid!
Welcome to footheory.com.  The bloggers and contributing members on this site are consultants, project/program managers and software architects working across the US.  Our community will focus on Microsoft technologies, .NET architecture, software patterns & practices and just plain stream of consciousness.

Bennie's Weblog

New Features in C# 3.0. Part 2

Introduction

In the previous section of this post, we looked at implicitly typed local variables, and how they enable us to write rich LINQ queries. In this section, we take a look at  another extension to the C# 3.0 type system, namely anonymous types. If you have not read the section on implicitly typed local variables yet, I recommend that you read that section first, since we will be referring to a number of the concepts mentioned in that post.

The code for this sample can be downloaded from our downloads section.

Anonymous Types

Problem Definition

In the previous versions of C#, if you wanted to represent a Pet, you typically would have to author a class  that looks something like this:

1 public class Pet 2 { 3 private string m_furColor; 4 private string m_ownerName; 5 private string m_name; 6 private long m_tagNumber; 7 private int m_weight; 8 private int m_height; 9 10 // public read/write properties here.. 11 12 // constructors here... 13 14 } // class Pet

While the IDE now offers a lot of refactoring and other editing tools, and C# 3.0 offers now offers a great time-saving feature called "automatic properties", writing a class like the one shown above is still very time consuming and tedious (some would call it 'monkey work' ;-).

When we are writing LINQ queries, we will be writing code, the structure of which looks like this:

var query = from p in ListOfPets select new { p.FurColor, p.TagNumber, p.OwnerName };

The key here is that we are querying arbitrary groups a data (in this case "FurColor, TagNumber and OwnerName). When I am querying "Pets", and the selected fields are a subset of the Pet class, then it would be quite a bit of work if we first had to completely define the structure of this "class", or "clumb of data", before I could use this class. Especially, because we would not have a need for this class outside of the scope of the query. What we really need is the ability to define arbitrary structures of data on the fly at runtime.

The Solution: Anonymous Types

The solution to the problem described above is offered by anonymous types. Anonymous types allow you to represent arbitrary groups of related data, without having the requirement to declare a class with it's detailed structure first. Below is an example.

var donBox = new { FirstName="Don", LastName="Box", Height="190", Weight="150" };

The above line of code will work even if you would not have defined a Pet class in advance. Anonymous methods let you arbitrarily create new structures to represent your data, without having to define them at "coding time" first.

The above line of code simply generates a class behind the scene (You never really see it, although I create a utility that lets you do some "spelunking" around in an anonymous class, see later). This generated anonymous class would look something like this:

[CompilerGenerated, DebuggerDisplay(@"\{ Weight = {Weight}, Height = {Height}, FirstName = {FirstName}, LastName = {LastName} }", Type="<Anonymous Type>")] internal sealed class <>f__AnonymousType0<<Weight>j__TPar, <Height>j__TPar, <FirstName>j__TPar, <LastName>j__TPar> { // Fields [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly <FirstName>j__TPar <FirstName>i__Field; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly <Height>j__TPar <Height>i__Field; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly <LastName>j__TPar <LastName>i__Field; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly <Weight>j__TPar <Weight>i__Field; // Methods [DebuggerHidden] public <>f__AnonymousType0(<Weight>j__TPar Weight, <Height>j__TPar Height, <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 <Height>j__TPar Height { get; } public <LastName>j__TPar LastName { get; } public <Weight>j__TPar Weight { get; } } Expand Methods

Notice that I was lazy and used reflector to generate this source code

As you can see, the compiler does generate a (albeit complex) name for anonymous class, and it has all of the fields and properties that we expect. Note that each of the fields in the anonymous type is really of a "var", so it is really an implicitly typed local variable. But the important fact is that this class is a real valid .NET type, with the same features and the hand-crafted class shown earlier.

Note that anonymous types combined with LINQ now allows us to query for an arbitrary set of data, without the need to first declare the structure of the queried data (which really would have been a non-starter for LINQ).

Applied Anonymous Types

Below is a simple C# 3.0 function that illustrates some of the anonymous type functionality:

1 /// <summary> 2 /// This method demonstrates some anonymous types features 3 /// </summary> 4 private static void createSampleAnonymousTypes() 5 { 6 7 var v1 = new { Weight = 180.5, Height = 170, FirstName = "Neil", LastName = "Armstrong" }; 8 var v2 = new { Weight = 145.2, Height = 190, FirstName = "Demi", LastName = "Moore" }; 9 10 Console.WriteLine("Contents of v1: '{0} {1}'", v1.FirstName, v1.LastName); 11 Console.WriteLine("\tWeight: {0}, Height: {1}, FirstName: {2}, LastName: {3}", 12 v1.Weight, v1.Height, v1.FirstName, v1.LastName); 13 14 Console.WriteLine("Contents of v2: '{0} {1}'", v2.FirstName, v2.LastName); 15 Console.WriteLine("\tWeight: {0}, Height: {1}, FirstName: {2}, LastName: {3}", 16 v2.Weight, v2.Height, v2.FirstName, v2.LastName); 17 18 Console.WriteLine(Environment.NewLine); 19 Console.WriteLine("Type of v1:\n {0}", v1.GetType()); 20 Console.WriteLine("Type of v2:\n {0}", v2.GetType()); 21 if (v1.GetType() == v2.GetType()) 22 { 23 Console.WriteLine("The types of v1 and v2 are identical!"); 24 } 25 } // method createSampleAnonymousTypes 26

In lines 7 and 8, we create two anonymous type instances (v1 and v2).  Note that  since the fields in both instances are identical in both name and type, the compiler will be smart enough to generate only one anonymous type (we will verify this later on in this method).

Note that you have to use an implicitly typed local variables (i.e. a "var") as the type of both instances. The compiler will be smart enough to match up the type of v1 and v2 to the name it generated for the anonymous type.

In lines 10 through 16 we are accessing the fields in our anonymous class instances, note that we can use the standard <variable Name>.<Field Name> syntax, since an anonymous type in still a standard .NET types, with all of its features.

In lines 18 through 20, we are writing out the type names of both v1 and v2, and in the final lines of the functions, we indeed verify that the both types are indeed identical.

The output of this function is shown below:

consoleAnonymousTypes

When you take a closer look at how the type name of the anonymous type is built, we notice it uses:

  • The "<>f__AnonymousType" prefix
  • The CLR types names of the fields:
    • System.Double for the "Weight" field
    • System.Int32 for the "Height" field
    • System.String for both first and last name

and again, as we mentioned before, the compiler noticed that both anonymous types contain the same fields, so it only generated one type.

Anonymous Types in Depth

 Now, to REALLY verify that the compiler indeed generates a new class, a seasoned developer will reach into his/her toolbox and use ILDASM, and open up the compiled assembly. If you expand the tree view, this is what you will see:

ILDASMAnonmounsTypes

When you look closely at the full name, you will recognize the "<f>__AnonymousType" prefix. You will also notice that the fields (FirstName, LastName, Height and Weight) have been generated, together with their property getters and setters.

Note also that since the anonymous type inherits from object (after all, it IS a valid .NET class, and any .NET class inherits from Object), you see the overrides for the Equals(), GetHashCode() and ToString() methods.

Finally, the anonymous type contains a constructor, which takes the values of the fields as arguments, and sets the field values based upon the passed-in parameters.

The dumpAnonumousType() method

Now, it is not always simple to look at the details at ILDASM, and for that purpose I created a simple routine which takes an anonymous type instance, and writes out a human-readable presentation on the console.

Note that when I say that it "takes an anonymous type instance" this should create some questions for you. Before, I mentioned that an anonymous type instance is always an implicitly typed local variable, so how can I pass it to a function. The trick here is to not forget that an anonymous type still derives from System.Object, so you can still pass it as an object, as is shown below:

1 var v1 = new { Weight = 180.5, Height = 170, FirstName = "Neil", LastName = "Armstrong" }; 2 dumpAnonymousType(v1); 3 4 } 5 6 /// <summary> 7 /// This method has the ability to dump the most important 8 /// characteristics of an anonymous type 9 /// </summary> 10 /// <param name="myTypeValue"></param> 11 private static void dumpAnonymousType(object myTypeValue) 12 { 13 ..... 14 } // method dumpAnonymousType

so, we see that our dumpAnonymousType() method takes a object reference instead of a "var", which would generated a compile error.

The implementation of the dumpAnonymousType method is very straightforward. It uses standard reflection to parse apart the fields, properties and methods, as shown below:

1 private static void dumpAnonymousType(object myTypeValue) 2 { 3 // These are the standard binding flags that we are going to use 4 BindingFlags flags = 5 BindingFlags.Public | 6 BindingFlags.NonPublic | 7 BindingFlags.Instance; 8 9 Type myType = myTypeValue.GetType(); 10 Console.WriteLine("Type: {0}", myType); 11 12 // Write out the fields 13 Console.WriteLine("\tType Fields: "); 14 foreach (MemberInfo member in myType.GetFields(flags)) 15 { 16 FieldInfo fi = member as FieldInfo; 17 var myValue = fi.GetValue(myTypeValue); 18 Console.WriteLine("\t\tMember: {0}, Member Type: {1}, Data Type: {2}, Value: {3}", 19 member.Name, 20 member.MemberType.ToString(), 21 myValue.GetType().Name, 22 myValue.ToString()); 23 } 24 25 // Write out the properties 26 Console.WriteLine("\tType Properties: "); 27 foreach (MemberInfo member in myType.GetProperties(flags)) 28 { 29 PropertyInfo pi = member as PropertyInfo; 30 var myValue = pi.GetValue(myTypeValue, null); 31 32 Console.WriteLine("\t\tMember: {0}, Member Type: {1}, Data Type: {2}, Value: {3}", 33 member.Name, 34 member.MemberType.ToString(), 35 myValue.GetType().Name, 36 myValue.ToString()); 37 } 38 39 // Write out the methods 40 Console.WriteLine("\tType Methods: "); 41 foreach (MemberInfo member in myType.GetMethods(flags)) 42 { 43 Console.WriteLine("\t\tMember: {0}, Member Type: {1}, Data Type: {2}", 44 member.Name, 45 member.MemberType.ToString(), 46 member.GetType().UnderlyingSystemType.Name); 47 } 48 } // method dumpAnonumousType

The interesting part of this method is that when your get a field or a property, and you want to get the value of the field or property, you have to use an implicitly typed local field (i.e. a "var"), since internally, they really are stored as such (as you can see in the reflector and ILDASM output). Since I use the indirection of first converting the field or type to a var, and then getting its Type info, I can show the "real type" in my output.

 

A sample output is shown below:

dumpOutput

Conclusion

Anonymous Types are a very important feature in the toolset of C# 3.0, and is on the features that enables us to write LINQ queries (together with Lambda BLOCKED EXPRESSION. They have the ability to greatly reduce to amount of code we have to write if  we want to perform a "one-time" processing of a logical "clump" of data.

Comments

 

Bennie's Weblog said:

Introduction This post does not focus on one particular new feature is the rich new feature set of C#

August 2, 2007 11:05 PM

Leave a Comment

(required)  
(optional)
(required)  
Add

About bennie

I work for a Microsoft Gold Partner, Statera SouthWest as a Strategic Partner and a Solutions Architect
Copyright ASIQS Corporation © 2006, All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems