Understanding Anonymous Types                                                 
     
As an object-oriented programmer, you know the benefits of defining classes and structures to represent the
state and functionality of a given programming entity. Whenever you need to define a user-defined type that
is intended to be reused across projects, creating a new class or structure is common practice. Many of these
classes provide numerous bits of functionality through a set of methods, events, properties, and custom
constructors.

Other times, you may wish to define a class simply to model a set of encapsulated and somehow related data
points without any associated functionality. Imagine you wish to define the ‘shape’ of data (three strings and
one integer) but have no need to define custom members (methods, events, and so on). Furthermore, what if
this type is only used internally by a small part of your application and is not intended to be reused? If you
need such a temporary type, you would need to build a new class definition by hand:


class SomeClass
{
// Define a set of private member variables.

// Make a property for each member variable.

// Override ToString() to account for each member variable.

// Override Equals() to account for value-based semantics.

// Override GetHashCode().
}


.NET 3.5 now allows you to define an ‘anonymous type’. This can be very helpful when you simply wish to
define the ‘shape’ of a type without any custom functionality. Essentially, this allows you to build UDTs
(user-defined types) on the fly without literally defining the class or structure in code. In most programming
assignments, this feature will be most useful when interacting with LINQ technologies.

When you define an anonymous type, you do so by making use of implicitly typed local variables in
conjunction with object initialization syntax. Each name/value pair can be accessed after the fact using CLR
property syntax. Consider the following anonymous type that models a simple automobile type:


// C#
static void CreateAndUseAnonymousType()
{
// Make an anonymous type representing a car.
var myCar =
  new { Color = "Black", Make = "Saab", CurrentSpeed = 55 };

Console.WriteLine("My car is the color {0}.", myCar.Color);
}











' VB
Sub CreateAndUseAnonymousType()

' Make an anonymous type representing a car.
Dim myCar = New With {.Color = "Black", .Make = "Saab", _
                      .CurrentSpeed = 55}

Console.WriteLine("My car is the color {0}.", myCar.Color)
End Sub


C# and VB differ radically on the underlying treatment of the name/value pairs defined by an anonymous
type. In C#, each name/value pair results in
read-only properties. In VB, each name/value pair results in
read-write properties.

Consider the following difference in overall functionally. Notice how it is a compiler error to change the state
of an anonymous type instance in C#.


// C#
static void CreateAndUseAnonymousType()
{
// C# underlying properties are read only!
var myCar = new { Color = "Black", Make = "Saab", CurrentSpeed = 55 };

myCar.Color = "Pink"; // ERROR in C#!

Console.WriteLine("My car is the color {0}.", myCar.Color);
}




' VB
Sub CreateAndUseAnonymousType()

' VB underlying properties are read/write!
Dim myCar = New With {.Color = "Black", .Make = "Saab", _
                      .CurrentSpeed = 55}

myCar.Color = "Pink"  ' OK in VB!

' Color would now be "Pink"
Console.WriteLine("My car is the color {0}.", myCar.Color)
End Sub


Internal Composition of Anonymous Types
All anonymous types are automatically derived from System.Object and, therefore, support each of the
members provided by this base class. Given this, you could invoke
ToString(), GetHashCode(), Equals(),
or
GetType() on the anonymous myCar variable. Consider the following helper method, which invokes
various members of
Object for diagnostic purposes.


// C# (VB code would be similar)
static void ReflectOverAnonymousType(object obj)
{
 Console.WriteLine("obj is an instance of: {0}",
   obj.GetType().FullName);
 Console.WriteLine("Base class of {0} is {1}",
   obj.GetType().Name, obj.GetType().BaseType);
 Console.WriteLine("obj.ToString() = {0}", obj.ToString());
 Console.WriteLine("obj.GetHashCode() = {0}", obj.GetHashCode());
 Console.WriteLine();
}


Now, consider the following usage and resulting output.


// C# (VB code would be similar)
static void Main(string[] args)
{
 // Make an anonymous object representing a car.
 var myCar = new {Color = "Black", Make = "Saab", CurrentSpeed = 55};

 // Reflect over what the compiler generated.
 ReflectOverAnonymousType(myCar);
}












Here, the anonymous type has been assigned a unique name, which Is-A System.Object. Understand that the
assigned type name is completely determined by the compiler and is not accessible in your code base. Your
type names will differ.

Beyond the autogenerated name, each compiler-generated type has the following characteristics:
     •        A default constructor.
     •        The object initialization name/value pairs result in a public property and private read-only backing
               field.
     •        The type is sealed for security reasons.
     •        Each virtual member of
System.Object has been overridden.

The compiler-generated implementation of ToString() simply makes use of StringBuilder to return the current
set of name/value pairs. Again, the names of the properties are a direct result of the anonymous type
definition.


// Approximate C# code.
public override string ToString()
{
 StringBuilder builder1 = new StringBuilder();
 builder1.Append("{");
 builder1.Append("Color");
 builder1.Append("=");
 builder1.Append(this.Color);
 builder1.Append(", ");
 builder1.Append("Make");
 builder1.Append("=");
 builder1.Append(this.Make);
 builder1.Append(", ");
 builder1.Append("CurrentSpeed");
 builder1.Append("=");
 builder1.Append(this.CurrentSpeed);
 builder1.Append("}");
 return builder1.ToString();
}


The compiler-generated implementation of GetHashCode() returns a hash value based on the hash values of
each named property:


// Approximate C# code.
public override int GetHashCode()
{
 int hashValue = 0;
 if (this.Color != null)
 {
   hashValue ^= this.Color.GetHashCode();
 }
 if (this.Make != null)
 {
   hashValue ^= this.Make.GetHashCode();
 }
 return (hashValue ^ this.CurrentSpeed.GetHashCode());
}


The compiler-generated implementation of Equals() is a bit more interesting. The Equals() method is
implemented to make use of value-based semantics. Therefore, if you have two anonymous types that have
identical name/value pairs,
Equals() returns true. However, the compiler does not overload the equality
operators for an anonymous type.

Recall that by default, the C# equality operators are making use of
reference-based semantics. Thus, if you
use these C# operators to test for equality of two anonymous types, the return value is
false.

However, VB anonymous types can only be tested for equality by calling Equals(). Unlike C#, it is a
compiler error to use the VB equality operators to test anonymous types. If you need to test for reference-
based equality in VB, call the shared
Object.ReferenceEquals() method.

Consider the following code and resulting output. Notice that
firstCar and secondCar have identical
name/value pairs.


// C#
// Make two anonymous classes with identical name/value pairs.
var firstCar = new { Color = "Bright Pink",
 Make = "Saab", CurrentSpeed = 55 };
var secondCar = new { Color = "Bright Pink",
 Make = "Saab", CurrentSpeed = 55 };

// Are they considered equal when using Equals()? Yes!
if (firstCar.Equals(secondCar))
   Console.WriteLine("Same anonymous object!");
else
   Console.WriteLine("Not the same anonymous object!");

// Are they considered equal when using ==? No!
// VB programmers would need to call
// Object.ReferenceEquals(firstCar, secondCar)
if (firstCar == secondCar)
   Console.WriteLine("Same anonymous object!");
else
   Console.WriteLine("Not the same anonymous object!");

// Are these objects the same underlying type? Yes!
if (firstCar.GetType().FullName == secondCar.GetType().FullName)
   Console.WriteLine("We are both the same type!");
else
   Console.WriteLine("We are different types!");












Anonymous types can be composed using additional anonymous types. For example, consider the following
entity, which models a purchase order for a given automobile. The screen shot is the result of calling

ToString()
on the purchaseItem object.


// C#
// Make an anonymous type that is composed of another.
var purchaseItem = new {
 TimeBought = DateTime.Now,
 ItemBought = new {Color = "Red", Make = "Saab", CurrentSpeed = 55},
 Price = 34.000};






' VB
' Make an anonymous type that is composed of another.
Dim purchaseItem = New With { _
 .TimeBought = DateTime.Now, _
 .ItemBought = New With {.Color = "Red", .Make = "Saab",
.CurrentSpeed = 55}, _
 .Price = 34.0}


When working with anonymous types, remember the following limitations:
     •        You don’t control the name of the anonymous type.
     •        Anonymous types always extend
System.Object.
     •        Anonymous types cannot support interfaces, events, custom methods, custom overrides, or
               overloaded operators.
     •        Anonymous types are always implicitly sealed.
     •        Anonymous types are always created using the default constructor.
     •        The generated C# properties are read-only, while VB generated properties are read/write.

Given these limitations, remember this feature is only useful when you wish to quickly model the ‘shape’ of
a type, not its functionality. Like many of the topics shown in this chapter, anonymous types are quite
useful when working with LINQ technologies. For example, using anonymous types, you can build LINQ
queries that project new forms of data from a container. Whenever you need to model behaviors and
functionality, don’t bother with an anonymous type. Just make a traditional class/structure.
Anonymous Types
Table of Contents
Copyright (c) 2008.  Intertech, Inc. All Rights Reserved.  This information is to be used exclusively as an online
learning aid.  Any attempts to copy, reproduce, or use for training is strictly prohibited.
Courseware
Training Resources
Tutorials
Services