
Welcome back my 'nog-froth
mustachioed friends! I've returned with another helping of
Refactor! Pro
goodness for Visual
Studio 2008. One scrooge
commented that the last present was a little weak, so I've decided to give a bigger
gift this time. However, I will be saving some of my bestest presents for the very
end.
So, sit back, relax and take out your ear plugs as I serenade you with my third
verse.
"On the third day of X-mas my true love (DevExpress)
gave to me..."
Name Anonymous Type
Of all the new features in
C#
3.0 and
Visual Basic 9, Anonymous
Types is one of the most convenient. This feature allows the user to create
new objects "on the fly," without providing type definitions for them. For example:
var person = new
{ Name = "St. Nick", Age =
"Really Old" };
Console.WriteLine(person.Name);
When the C# compiler encounters the code above, it generates a new type with
two string properties:
Name
and Age.
This powerful new feature is not without limitations. Because anonymous types
have no accessible type name (they're anonymous - duh!), they can only be referenced
by variables that are implicitly-typed. This becomes very frustrating when using anonymous types in other natural ways.
public static ??? CreatePerson()
{
return new
{ Name = "St. Nick", Age =
"Really Old" };
}
What should I fill in for
??? in the code above? One possibility is to use
object. However, if I do that, how do I access the properties from client
code? Reflection? An awkward
casting
helper?
Recently, this very problem was hashed out on the
MSDN forums. The general consensus was, when you want to expose an anonymous
type in an API (e.g. return an anonymous type from a method), swap it out for a
defined type. However, defining a new type that matches an anonymous type is cumbersome.
Thankfully, Refactor!
Pro can step in and do this work for you. When the Name Anonymous Type refactoring
is applied to the anonymous type above, the following class is generated:
[DebuggerDisplay("\\{
Name = {Name}, Age = {Age} \\}")]
public sealed class Person:
IEquatable<Person>
{
private readonly string m_Name;
private readonly string m_Age;
public Person(string
name, string age)
{
m_Name = name;
m_Age = age;
}
public override bool Equals(object
obj)
{
if (obj
is Person)
return Equals((Person)obj);
return false;
}
public bool
Equals(Person obj)
{
if (!EqualityComparer<string>.Default.Equals(m_Name,
obj.m_Name))
return false;
if (!EqualityComparer<string>.Default.Equals(m_Age,
obj.m_Age))
return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<string>.Default.GetHashCode(m_Name);
hash ^= EqualityComparer<string>.Default.GetHashCode(m_Age);
return hash;
}
public override string ToString()
{
return String.Format("{{
Name = {0}, Age = {1} }}", m_Name, m_Age);
}
public string
Name
{
get
{
return m_Name;
}
}
public string
Age
{
get
{
return m_Age;
}
}
}
View Screencast of Name Anonymous Type in Action!
You might be thinking, "Wow! That's a lot of code! Is all of that really necessary?"
The answer is, yes, all of that code is necessary to produce a type definition that
is equivalent to the subtle features of an anonymous type. In C#, anonymous types
are immutable so we must generate read-only fields and properties. In addition,
the equality and identity of anonymous types are explicitly defined so they
can be compared with one another. In other words, C# anonymous types have value-type
semantics. The following code sample might clarify this:
var person1 = new
{
Name = "Dustin Campbell",
Age = "32"
};
var person2 = new
{
Name = "Dustin Campbell",
Age = "32"
};
var person3 = new
{
Name = "Dustin's Trophy Wife",
Age = "Undisclosed (but probably really, really
young)"
};
Console.WriteLine(person1.Equals(person2));
Console.WriteLine(person2.Equals(person3));
Because of the value-type characteristics of anonymous types, the above code
outputs the following to the console.
True
False
Of course, if Name Anonymous Type is applied, the same output is produced.
Traditionally, the Visual Basic team likes to make life hard for us, and anonymous
types are no exception. Contrary to C#, the Visual Basic compiler generates
mutable anonymous types. In addition, Visual Basic allows for partially-mutable
anonymous types when the
Key keyword is applied. (This is further proof that C# and Visual Basic have
very different agendas and destinies.) Fortunately, Name Anonymous Type is intelligent
enough to handle these differences.
Dim person = New With {.Name =
"St. Nick", .Age = "Really Old"}
When applied to the above code, Name Anonymous Type generates a new
Person class like so:
<DebuggerDisplay("\{ Name = {Name}, Age = {Age}
\}")> _
Public NotInheritable Class Person
Private m_Name
As String
Private m_Age
As String
Public Sub New(ByVal
name As String,
ByVal age As String)
m_Name = name
m_Age = age
End Sub
Public Overrides Function ToString()
As String
Return String.Format("{{
Name = {0}, Age = {1} }}", m_Name, m_Age)
End Function
Public Property
Name() As String
Get
Return m_Name
End Get
Set(ByVal
value As String)
m_Name = value
End Set
End Property
Public Property
Age() As String
Get
Return m_Age
End Get
Set(ByVal
value As String)
m_Age = value
End Set
End Property
End Class
To be consistent with the reference-type semantics of Visual Basic anonymous
types, Name Anonymous Type produces a mutable class. Gone are the overrides to
Equals()
and GetHashCode().
In addition,
the properties are read-write. It's this sort of
language independence that makes
Refactor! Pro
a choice tool for
Visual Studio
2008 development.
And that wraps up another verse in my holiday sing-a-long! Remember that the
features I am showing can be used right now. There's no need
to wait for some forthcoming beta. You can use them today. Until
next time...