Anonymous Types

Typically, custom Classes are created to represent entities (such as an Employee).  These classes can store information, and perform actions for their respective entity.  Structures are a lighter weight alternative to classes, and are used to define a composite value type.

 

A possible example of a custom “Employee” class with an ID property and a Name property:

 

Public Class Employee

    'Define the Employee ID
    Private mID As Integer
    Public Property ID() As Integer
        Get
            Return mID
        End Get
        Set(ByVal value As Integer)
            mID = value
        End Set
    End Property

    'Define the Employee Name
    Private mName As String
    Public Property Name() As String
        Get
            Return mName
        End Get
        Set(ByVal value As String)
            mName = value
        End Set
    End Property

End
Class

 

 

To use the Employee class, you must create a new instance of it, then set the appropriate properties:

 

Dim employee As New Employee()
employee.ID = 1
employee.Name = "Gary Lima"

 

 

Object Initializers…

VB 2008 introduced a new feature called Object Intializers, which in essence, allows you to set property values when you create a new instance of your class, not using the class’ constructor.

 

Object Initialization can only be used on classes that have constructors and support initialization with the “New” keyword.

 

To use Object Initialization in code, you must use the new “With” keyword, beginning and ending curly braces “{}”, and provide the name of the property beginning with a period (“.”), plus the value you wish to set it to.

 

Here’s an example of how Object Initialization is used to create a new instance of the Employee class, setting the ID and Name properties at the same time:

 

'Using Object Initialization to set
'  the ID and Name properties
Dim employee As New Employee() _
    With {.ID = 1, .Name = "Gary Lima"}

 

Note:  Although the above examples appears to be 2 lines, it’s actually considered 1 line by the compiler, because the line continuation character (“_”) is used at the end of the first line.

 

 

Anonymous Types…

There may be times when you could use a light-weight class to store information for something like an Employee, but really don’t want to create an Employee Class or Structure.  Perhaps an Employee Class wouldn’t be needed anywhere else.  And you don’t need methods, events or any other custom functionality.  In times like these, you can consider using an Anonymous Type.

 

Anonymous Types are, essentially, anonymous.  Not to insult your intelligence or anything…  Merriam-Webster’s online dictionary defines anonymous as “1:  Not named or identified”.  That’s a perfect description, because when you declare an Anonymous Type, you don’t name or identify it’s type.  Not specifying the type is actually using implicit typing, which allows the compiler to determine the type of the variable based off of the initialization value. 

 

In the case of an Anonymous Type, the compiler will actually generate a class for you, that has no usable name and is not directly accessible from code.  The class will automatically have all of the properties that were defined in the required Object Initialization syntax.

 

For example, let’s look at declaring an Anonymous Type for an employee:

 

'An Anonymous Type (no type declared)
'  Notice "As Employee" is missing.
Dim employee = New _
        With {.ID = 1, .Name = "Gary Lima"}

 

So some points about the example above:

 

     -  No type was declared (“As Employee” not used)

     -  The Equal Sign (“=”) was used

     -  The “New” keyword was used

     -  The “With” keyword was used

     -  Curly Braces were used “{}”

     -  The properties were specified using a period “.” (.ID, .Name)

     -  The properties were initialized to the specified values.

     -  The properties were separated by a comma (“,”)

 

 

Once an Anonymous Type has been created, accessing the properties is just like accessing the properties of any ordinary class.  An example using the “employee” from above:

 

'Displays "1 Gary Lima"
MsgBox(employee.ID & " " & employee.Name)

 

 

Additionally, the property values can be modified, just like the property values of any ordinary class can be modified.  For example:

 

employee.ID = 2
employee.Name = "VBRocks"

'Displays "2 VBRocks"
MsgBox(employee.ID & " " & employee.Name)

 

 

Key Properties

It is possible to define a property as being a “Key” property.  Key properties are different than regular properties, because they are read-only, the values of Key properties are used for equality comparison, and they are included in the hash code algorithm (which we’ll see later).

 

Let’s revisit the “employee” example above, and this time use the “Key” keyword to define the “ID” property as a Key property:

 

'An Anonymous Type (no type declared)
'  The ID property is a "Key" property
Dim employee = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

 

 

Now that we have an employee Anonymous Type created with the “ID” property as a “Key” property, let’s take a look at using it in code:

 

'Error: Can't set the ID, because it's a Key
'  property.  Trying to will generate a
'  compiler error
'employee.ID = 2

'Ok to change the name

employee.Name = "VBRocks"

'Displays "1 VBRocks"
MsgBox(employee.ID & " " & employee.Name)

 

 

Just as a note, any property can be defined as a Key property.  Furthermore, you can define as many properties as you want as Key properties.  For example:

 

'The ID property is a "Key" property
'  The Name property is a "Key" property
Dim employee = New _
        With {Key .ID = 1, Key .Name = "Gary Lima"}

 

 

Inheritance

Anonymous Types are directly derived from System.Object.  Because this is true, there are 5 methods that it inherits:  Equals, GetHashCode, GetType, ReferenceEquals, and ToString.

 

If an Anonymous Type contains at least 1 Key property then Equals, GetHashCode and ToString are overridden.  If an Anonymous Type contains no Key properties, then only ToString is overridden.

 

 

Equals

Anonymous Types that have at least 1 Key property defined implement the System.IEquatable(T) interface, which means the .Equal method can be used to determine equality of instances.

 

There are some important things to understand before attempting to use the .Equal method to test equality of Anonymous Types.  Instances of an Anonymous Type are considered equal if:

 

     -  They are declared in the same assembly

     -  They have the same properties, same inferred types, and same order

     -  They have the same Key properties

     -  They have at least 1 Key property

 

So having considered that, let’s take a look at some examples of testing equality.

 

This example returns False (Not Equal), because there is no Key property for each Anonymous Type:

 

'No Key property
Dim employee1 = New _
        With {.ID = 1, .Name = "Gary Lima"}

'No Key property
Dim employee2 = New _
        With {.ID = 1, .Name = "Gary Lima"}

'Displays "False"
MsgBox(employee1.Equals(employee2))

 

 

This example returns False (Not Equal), because both Anonymous Types do not have the same Key property defined, (only 1 does):

 

'Key property:  ID
Dim employee1 = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

'No Key property
Dim employee2 = New _
        With {.ID = 1, .Name = "Gary Lima"}

'Displays "False"
MsgBox(employee1.Equals(employee2))

 

 

This example returns True (Equal), because both Anonymous Types have the same Key property defined, and they are set to the same value.

 

'Key property:  ID
Dim employee1 = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

'Key property:  ID
Dim employee2 = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

'Displays "True"
MsgBox(employee1.Equals(employee2))

 

 

This example returns False (Not Equal), because both Anonymous Types do not have the same value for each Key property:

 

'Key property:  ID
Dim employee1 = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

'Key property:  ID
Dim employee2 = New _
        With {Key .ID = 2, .Name = "Gary Lima"}

'Displays "True"
MsgBox(employee1.Equals(employee2))

 

 

This example returns True (Equal), because both Anonymous Types have the same Key properties set to the same value, even though the Name is different:

 

'Key property:  ID
Dim employee1 = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

'Key property:  ID
Dim employee2 = New _
        With {Key .ID = 1, .Name = "VBRocks"}

'Displays "True"
MsgBox(employee1.Equals(employee2))

 

 

This example returns False (Not Equal), because both Anonymous Types do not have the exact same initializer list of properties:

 

'Key property:  ID
Dim employee1 = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

'Key property:  ID
Dim employee2 = New _
        With {Key .ID = 1, .Name = "Gary Lima", _
              .Department = 1}

'Displays "False"
MsgBox(employee1.Equals(employee2))

 

 

GetHashCode

Anonymous Types that do not have a Key property defined return a hash code computation specific to each type, and based on 1 or more of the values of each property. 

 

Therefore, Anonymous Types with no Key property defined will never produce a hash code that matches another Anonymous Type, even if they both have all of the same properties, the same inferred types, and are in the same order.

 

So for example:

 

'No Key property
Dim employee1 = New _
        With {.ID = 1, .Name = "Gary Lima"}

'No Key property
Dim employee2 = New _
        With {.ID = 1, .Name = "Gary Lima"}


'employee1.GetHashCode() = 31914638
'employee2.GetHashCode() = 18796293

 

 

Anonymous Types that have 1 or more Key properties defined return a hash code computation specific to each type, and based on the value of each Key property.

 

Therefore, Anonymous Types with a Key property defined will produce a hash code that matches another Anonymous Type that has all of the same properties, the same inferred types, are in the same order, and have the same Key properties that have the same values.

 

So for example:

 

'ID Key property
Dim employee1 = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

'ID Key property
Dim employee2 = New _
        With {Key .ID = 1, .Name = "VBRocks"}


'employee1.GetHashCode() = -286022222
'employee2.GetHashCode() = -286022222

 

Remember, that the example above produces identical hash codes, because it is only using Key property values to compute it’s code.  Additionally, both Anonymous Types are Equal, because they have the same Key property values.

 

 

ToString

The ToString method is the simplest of the overridden methods, returning a string comprised of each property/value pair, enclosed in curly braces “{}”, as you can see:

 

Dim employee1 = New _
        With {Key .ID = 1, .Name = "Gary Lima"}

Dim employee2 = New _
        With {.ID = 1, .Name = "Gary Lima"}

'employee1.ToString() =
'        "{ ID = 1, Name = Gary Lima }"

'employee2.ToString() =
'        "{ ID = 1, Name = Gary Lima }"

 

Notice there was no difference in the ToString output, even though employee1 has a Key property.

 

 

Anonymous Types and LINQ…

Although you may find Anonymous Types interesting and useful, they are realistically not the method of choice to work with entity data.  Standard object oriented programming practices lead to defining entities using Classes, while Interfaces and Structures can be defined and used when and where necessary.

 

The true purpose of Anonymous Types is to support Language-Integrated Query (LINQ).  To this end, they are a perfect match.

 

In a nutshell, LINQ is a structured query language that allows a programmer to write queries against strongly typed collections of objects using Visual Basic keywords and operators.

 

The result of a LINQ query is returned as an Anonymous Type, because the type of the result cannot be defined in advance.  So, the compiler creates a data type matching the properties and their order.

 

Unfortunately, we’re going to keep the introduction to LINQ brief here, but explain just enough to help you understand how Anonymous Types are used with LINQ.

 

Therefore, to assist with this example:  The LINQ query syntax is similar to standard SQL syntax.  In its most basic sense, it says:  SELECT * FROM collection.  In SQL, the collection is usually a table, but in VB it’s a collection of objects.  The syntax is just a little different in VB, because in order for LINQ to support Intellisense, it has to know what type it’s working with.  So in VB the equivalent syntax is:  FROM collection SELECT *.

 

Now that you have a grasp on that…  let’s look at some examples.  (And if your eyes are rolling, and you’re thinking “yeah, right…”, don’t worry about it.  You can get into LINQ later.  The important thing to realize is that it uses Anonymous Types).

 

Let’s start by creating a list of Employees that we can work with in following examples.  The Employees class was the very first example given at the very top of this article.

 

'Create a new list of Employees
Dim listOfEmployees As New List(Of Employee)

'Add 20 employees to our list
For i As Integer = 1 To 20

    'Add an Employee to the list
    listOfEmployees.Add( _
        New Employee _
        With { _
            .ID = i, _
            .Name = "Employee " & i})

Next

 

 

Next, let’s use LINQ to query the “listOfEmployees”:

 

'This LINQ query is saying:
'  SELECT ID, Name FROM listOfEmployees
Dim employees = _
    From emp In listOfEmployees _
    Select emp.ID, emp.Name

 

At this point, the “employees” variable is holding the definition of the query only, not the actual result, because the query has not been executed yet.

 

Notice that the “employees” variable in the example above does not have a type defined.  If you were to check the type by using “employees.GetType.Name”, you would find it contains a list iterator.

 

 

Now, let’s look at the execution of the LINQ query, and output the results to the Output Window. 

 

'Execute the query, and iterate through
'  the result
For Each emp In employees

    'emp is an Anonymous Type here
    Console.WriteLine("{0,-2} {1}", _
        emp.ID, emp.Name)

Next

 

It’s important to understand the the “emp” variable in the For Each structure is an Anonymous Type.  If we attempted to declare “emp” using “As Employee”, the compiler would have generated an error.

 

 

Ok, one final example before wrapping things up:

 

'This LINQ query is saying:
'  SELECT ID, Name FROM listOfEmployees
'  WHERE ID <= 10
Dim employees = _
    From emp In listOfEmployees _
    Where emp.ID <= 10 _
    Select emp.ID, emp.Name


'Execute the query, and iterate through
'  the result
For Each emp In employees

    'emp is an Anonymous Type here
    Console.WriteLine("{0,-2} {1,-15} {2}", _
        emp.ID, emp.Name, emp.GetType.Name)

Next

 

Notice that this time, the Name of the type was written to the Output window.  Further examination will reveal that it is an anonymous type.

 

 

Summary

As you have seen, Anonymous Types enable a programmer to create objects without writing a class definition or structure for the data type, allowing the compiler to generate the class.  The class that the compiler generates has no usable name, and is not directly accessible in code.

 

Once an Anonymous Type has been created, it can be referenced just like any ordinary class.  Non-Key properties can be read and written to, while Key properties can only be read once they’ve been set.  Key properties are also used for equality comparisons and hash code generation.

 

Finally, Anonymous Types were designed primarily to support LINQ, because the type of the query result cannot be determined in advance.

0 comments:

Leave a Reply

Translate

Google-Translate-Chinese (Simplified) BETA Google-Translate-English to French Google-Translate-English to German Google-Translate-English to Italian Google-Translate-English to Japanese BETA Google-Translate-English to Korean BETA Google-Translate-English to Russian BETA Google-Translate-English to Spanish

Tags