Anonymous Types (Visual Basic)

Visual Basic supports anonymous types, which enable you to create objects without writing a class definition for the data type. Instead, the compiler generates a class for you. The class has no usable name, inherits directly from Object, and contains the properties you specify in declaring the object. Because the name of the data type is not specified, it is referred to as an anonymous type.

The following example declares and creates variable product as an instance of an anonymous type that has two properties, Name and Price.

' Variable product is an instance of a simple anonymous type.
Dim product = New With {Key .Name = "paperclips", .Price = 1.29}

A query expression uses anonymous types to combine columns of data selected by a query. You cannot define the type of the result in advance, because you cannot predict the columns a particular query might select. Anonymous types enable you to write a query that selects any number of columns, in any order. The compiler creates a data type that matches the specified properties and the specified order.

In the following examples, products is a list of product objects, each of which has many properties. Variable namePriceQuery holds the definition of a query that, when it is executed, returns a collection of instances of an anonymous type that has two properties, Name and Price.

Dim namePriceQuery = From prod In products
                     Select prod.Name, prod.Price

Variable nameQuantityQuery holds the definition of a query that, when it is executed, returns a collection of instances of an anonymous type that has two properties, Name and OnHand.

Dim nameQuantityQuery = From prod In products
                        Select prod.Name, prod.OnHand

For more information about the code created by the compiler for an anonymous type, see Anonymous Type Definition.

Caution

The name of the anonymous type is compiler generated and may vary from compilation to compilation. Your code should not use or rely on the name of an anonymous type because the name might change when a project is recompiled.

Declaring an Anonymous Type

The declaration of an instance of an anonymous type uses an initializer list to specify the properties of the type. You can specify only properties when you declare an anonymous type, not other class elements such as methods or events. In the following example, product1 is an instance of an anonymous type that has two properties: Name and Price.

' Variable product1 is an instance of a simple anonymous type.
Dim product1 = New With {.Name = "paperclips", .Price = 1.29}
' -or-
' product2 is an instance of an anonymous type with key properties.
Dim product2 = New With {Key .Name = "paperclips", Key .Price = 1.29}

If you designate properties as key properties, you can use them to compare two anonymous type instances for equality. However, the values of key properties cannot be changed. See the Key Properties section later in this topic for more information.

Notice that declaring an instance of an anonymous type is like declaring an instance of a named type by using an object initializer:

' Variable product3 is an instance of a class named Product.
Dim product3 = New Product With {.Name = "paperclips", .Price = 1.29}

For more information about other ways to specify anonymous type properties, see How to: Infer Property Names and Types in Anonymous Type Declarations.

Key Properties

Key properties differ from non-key properties in several fundamental ways:

  • Only the values of key properties are compared in order to determine whether two instances are equal.

  • The values of key properties are read-only and cannot be changed.

  • Only key property values are included in the compiler-generated hash code algorithm for an anonymous type.

Equality

Instances of anonymous types can be equal only if they are instances of the same anonymous type. The compiler treats two instances as instances of the same type if they meet the following conditions:

  • They are declared in the same assembly.

  • Their properties have the same names, the same inferred types, and are declared in the same order. Name comparisons are not case-sensitive.

  • The same properties in each are marked as key properties.

  • At least one property in each declaration is a key property.

An instance of an anonymous types that has no key properties is equal only to itself.

' prod1 and prod2 have no key values.
Dim prod1 = New With {.Name = "paperclips", .Price = 1.29}
Dim prod2 = New With {.Name = "paperclips", .Price = 1.29}

' The following line displays False, because prod1 and prod2 have no
' key properties.
Console.WriteLine(prod1.Equals(prod2))

' The following statement displays True because prod1 is equal to itself.
Console.WriteLine(prod1.Equals(prod1))

Two instances of the same anonymous type are equal if the values of their key properties are equal. The following examples illustrate how equality is tested.

Dim prod3 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim prod4 = New With {Key .Name = "paperclips", Key .Price = 1.29}
' The following line displays True, because prod3 and prod4 are
' instances of the same anonymous type, and the values of their
' key properties are equal.
Console.WriteLine(prod3.Equals(prod4))

Dim prod5 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim prod6 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 423}
' The following line displays False, because prod5 and prod6 do not 
' have the same properties.
Console.WriteLine(prod5.Equals(prod6))

Dim prod7 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 24}
Dim prod8 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 423}
' The following line displays True, because prod7 and prod8 are
' instances of the same anonymous type, and the values of their
' key properties are equal. The equality check does not compare the
' values of the non-key field.
Console.WriteLine(prod7.Equals(prod8))

Read-Only Values

The values of key properties cannot be changed. For example, in prod8 in the previous example, the Name and Price fields are read-only, but OnHand can be changed.

' The following statement will not compile, because Name is a key
' property and its value cannot be changed.
' prod8.Name = "clamps"

' OnHand is not a Key property. Its value can be changed.
prod8.OnHand = 22

Anonymous Types from Query Expressions

Query expressions do not always require the creation of anonymous types. When possible, they use an existing type to hold the column data. This occurs when the query returns either whole records from the data source, or only one field from each record. In the following code examples, customers is a collection of objects of a Customer class. The class has many properties, and you can include one or more of them in the query result, in any order. In the first two examples, no anonymous types are required because the queries select elements of named types:

  • custs1 contains a collection of strings, because cust.Name is a string.

    Dim custs1 = From cust In customers
                 Select cust.Name
    
  • custs2 contains a collection of Customer objects, because each element of customers is a Customer object, and the whole element is selected by the query.

    Dim custs2 = From cust In customers
                 Select cust
    

However, appropriate named types are not always available. You might want to select customer names and addresses for one purpose, customer ID numbers and locations for another, and customer names, addresses, and order histories for a third. Anonymous types enable you to select any combination of properties, in any order, without first declaring a new named type to hold the result. Instead, the compiler creates an anonymous type for each compilation of properties. The following query selects only the customer's name and ID number from each Customer object in customers. Therefore, the compiler creates an anonymous type that contains only those two properties.

Dim custs3 = From cust In customers
             Select cust.Name, cust.ID

Both the names and the data types of the properties in the anonymous type are taken from the arguments to Select, cust.Name and cust.ID. The properties in an anonymous type that is created by a query are always key properties. When custs3 is executed in the following For Each loop, the result is a collection of instances of an anonymous type with two key properties, Name and ID.

For Each selectedCust In custs3
    Console.WriteLine(selectedCust.ID & ": " & selectedCust.Name)
Next

The elements in the collection represented by custs3 are strongly typed, and you can use IntelliSense to navigate through the available properties and to verify their types.

For more information, see Introduction to LINQ in Visual Basic.

Deciding Whether to Use Anonymous Types

Before you create an object as an instance of an anonymous class, consider whether that is the best option. For example, if you want to create a temporary object to contain related data, and you have no need for other fields and methods that a complete class might contain, an anonymous type is a good solution. Anonymous types are also convenient if you want a different selection of properties for each declaration, or if you want to change the order of the properties. However, if your project includes several objects that have the same properties, in a fixed order, you can declare them more easily by using a named type with a class constructor. For example, with an appropriate constructor, it is easier to declare several instances of a Product class than it is to declare several instances of an anonymous type.

' Declaring instances of a named type.
Dim firstProd1 As New Product("paperclips", 1.29)
Dim secondProd1 As New Product("desklamp", 28.99)
Dim thirdProd1 As New Product("stapler", 5.09)

' Declaring instances of an anonymous type.
Dim firstProd2 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim secondProd2 = New With {Key .Name = "desklamp", Key .Price = 28.99}
Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = 5.09}

Another advantage of named types is that the compiler can catch an accidental mistyping of a property name. In the previous examples, firstProd2, secondProd2, and thirdProd2 are intended to be instances of the same anonymous type. However, if you were to accidentally declare thirdProd2 in one of the following ways, its type would be different from that of firstProd2 and secondProd2.

' Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = 5.09}
' Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = "5.09"}
' Dim thirdProd2 = New With {Key .Name = "stapler", .Price = 5.09}

More importantly, there are limitations on the use of anonymous types that do not apply to instances of named types. firstProd2, secondProd2, and thirdProd2 are instances of the same anonymous type. However, the name for the shared anonymous type is not available and cannot appear where a type name is expected in your code. For example, an anonymous type cannot be used to define a method signature, to declare another variable or field, or in any type declaration. As a result, anonymous types are not appropriate when you have to share information across methods.

An Anonymous Type Definition

In response to the declaration of an instance of an anonymous type, the compiler creates a new class definition that contains the specified properties.

If the anonymous type contains at least one key property, the definition overrides three members inherited from Object: Equals, GetHashCode, and ToString. The code produced for testing equality and determining the hash code value considers only the key properties. If the anonymous type contains no key properties, only ToString is overridden. Explicitly named properties of an anonymous type cannot conflict with these generated methods. That is, you cannot use .Equals, .GetHashCode, or .ToString to name a property.

Anonymous type definitions that have at least one key property also implement the System.IEquatable<T> interface, where T is the type of the anonymous type.

For more information about the code created by the compiler and the functionality of the overridden methods, see Anonymous Type Definition.

See also