Introduction
Anonymous types use the Dim keyword but do not include a formal type—that you code—in the initialization statement. That is, the New keyword is used but there’s no class name. These are not invariant types; these are strong types but the class is code-generated by the compiler and emitted to MSIL (the binary assembly) as an anonymous type.
Anonymous types are emitted using the same definition if the field names, number, and order are identical. Anonymous types are generated using generics so the field types can differ and still the same anonymous types will be used. If there are differences in the right-hand-side definition—for example, you use more or fewer fields, change the name—called the member declaratory—or add special qualifiers such as a key, the anonymous types will vary.
Visual Basic anonymous types are mutable—the field values can be changed—which is the opposite of C# anonymous types. C# anonymous types are immutable. However, you can use the Key keyword to designate certain fields in VB9 as fixed (or immutable), and that’s what this article is about.
Defining Keyed Anonymous Types
Keyed anonymous types are very similar in the declaration style as un-keyed anonymous types. In the former, you are simply introducing the new Key keyword for each field you’d like keyed. Here are two examples; the first shows an anonymous type and the second shows the same type keyed.
Dim person = New With {.ID = 1, .Name = "Paul Kimmel", _ .Title = "Keyed Anonymous Types"}
Now here is a similar anonymous type, including a key:
Dim person = New With {Key .ID = 1, .Name = "Paul Kimmel", _ .Title = "Keyed Anonymous Types"}
In the second example, you are indicating that ID is a keyed field. The first difference is that ID, Name, and Title can all be assigned to in the first definition, but in the second ID is read-only. (If you look at the MSIL, you will see that the second anonymous type defines ID as private initonly, or read only, and that while Name and Title has getters and setters ID has a getter only. You can look at the MSIL with ILDASM or use a decompiler and disassembler like Reflector.)
Quite simply, all fields in anonymous types can appear on the left hand side of the assignment operator (=) unless that field was qualified with the Key keyword.
Understanding How Keys Are Treated
Remember, everything in .NET inherits from the Object class. Anonymous types inherit directly from Object; this means they get Equals, GetHashCode, ToString, and GetType methods. If an anonymous typed introduces a key, Equals and GetHashCode are overridden. Keyed anonymous types with the same code-generated class and same key are considered identical because keyed anonymous types use the key to determine hash-equality. Listing 1 contains some sample code you can play with to explore how anonymous types with and without keys behave. You are encouraged to look at the ILDASM to see how the compiler treats each variation.
Listing 1: Sample code for exploring keyed anonymous type relationships.
Module Module1 Sub Main() Dim NoKey = New With {.ID = 1, .Name = "Paul Kimmel", _ .Article = "Keyed AnonymousTypes in VB9"} Console.WriteLine(NoKey.GetType().Name) Dim NoKey2 = New With {.ID = "PAULKI01", .Name = "Paul Kimmel", _ .Article = "Keyed AnonymousTypes in VB9"} Console.WriteLine(NoKey2.GetType().Name) ' False Console.WriteLine(NoKey.Equals(NoKey2)) Dim HasKey = New With {Key .ID = 1, .Name = "Paul Kimmel", _ .Article = "Keyed AnonymousTypes in VB9"} ' ID, Name, Article are member declarators 'HasKey.ID = 2 ' error - ID is read only HasKey.Name = "Paul T. Kimmel" ' ok - Name and Article ' can change Dim HasKey2 = New With {Key .ID = 1, .Name = "Joe Swanson", _ .Article = "Keyed AnonymousTypes in VB9"} Console.WriteLine("Has Key hash: " + _ HasKey.GetHashCode().ToString()) Console.WriteLine("Has Key (2) hash: " + _ HasKey2.GetHashCode().ToString()) Console.WriteLine(HasKey.Equals(HasKey2)) Console.WriteLine(HasKey) Console.ReadLine() End Sub End Module
Summary
In summary, if two anonymous types in the same assembly (project, .DLL, or .EXE) have the same member declarator (or field) names, inferred types, the same field order, and the same key value, the equality test will return true. Different anonymous types with a key can be tested for equality but will be unequal, and the same anonymous types with no key will be unequal.
About the Author
Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his new book UML DeMystified from McGraw-Hill/Osborne. Paul is a software architect for Tri-State Hospital Supply Corporation. You may contact him for technology questions at pkimmel@softconcepts.com.
If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org. Glugnet has opened a users group branch in Flint, Michigan that began in August 2007. If you are interested in attending, check out the www.glugnet.org web site for updates.
Copyright © 2007 by Paul T. Kimmel. All Rights Reserved.