Compartilhar via


Tuples and Anonymous Types - VB

[Table of Contents] [Next Topic]

Anonymous types are a way that you can define, declare, and instantiate a type and an object in a single step.

Selection in a LINQ query expression is an operation of projection.  While projecting, you often want to create new types that are used only in the context of projection.  After you have processed the projection, you are not interested in using the types again.  Anonymous types allow you to define a class, and declare, and initialize an object of that class without giving the class a name.

This blog is inactive.
New blog: EricWhite.com/blog

Blog TOCAnonymous types are one of VB’s implementations of the idea of a tuple.  In computing, a tupple is a set of name/value pairs.  For example, the contents of an address in the Typical Purchase Order XML document could be expressed as:

Name: Ellen Adams
Street: 123 Maple Street
City: Mill Valley
State: CA
Zip: 90952
Country: USA

The term tuple originated from the sequence single, double, triple, quadruple, quintuple, n-tuple.  When you create an instance of an anonymous type, it is convenient to think of it as creating a tuple of order n.  If you write a query that creates a tuple in the select clause, then the query returns an IEnumerable of the tuple.

You create anonymous types using the VB 9.0 object initializer syntax.

A Simple Example

The following shows a very simple example of the creation of an anonymous type:

Dim x = New With { _
.Name = "Eric", _
.Phone = "555-1212" _
}
Console.WriteLine("x.Name:" + x.Name)
Console.WriteLine("x.Phone:" + x.Phone)

When you use an object initializer in this fashion, the VB compiler automatically generates a class with two public properties, Name and Phone.  The compiler infers the types of the two properties.  In this case, they are both of type string.

There is nothing so very special about this type - it is simply an instance of a reference type.  The only thing different about this type is that it has a name that you can't see.

This is a situation where it is required to use type inference.  You can't see the name for the type.  But by not specifying the type in the variable declaration, we can let the compiler infer the type.

Rest assured that the variable is still strongly typed.  For example, you can't assign it to a variable of an incompatible type.

An Anonymous Type used for Projection

A typical projection would be as follows.  If you have a class Customer:

Public Class Customer
Private m_name As String
Private m_address As String
Private m_phone As String
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 Address As String
Get
Return m_address
End Get
Set(ByVal value As String)
m_address = value
End Set
End Property
Public Property Phone As String
Get
Return m_phone
End Get
Set(ByVal value As String)
m_phone = value
End Set
End Property
End Class

You could create an anonymous type from this customer, as follows:

Dim c As Customer = GetCustomer()
Dim x = New With { _
.Name = c.Name, _
.Phone = c.Phone _
}

Projection of a Collection

When coding in the functional style, you will often take one collection of a type, and project a new collection of a different type.  Consider the following code, which takes a collection of Customer objects, and projects an anonymous type.  The code uses the Select extension method, which is what we use when we want to project a collection of a different type.

Dim custList() As Customer = { _
New Customer With { _
.Name = "Bob", _
.Address = "123 Main Street, Seattle, WA 98111", _
.Phone = "555-1234" _
}, _
New Customer With { _
.Name = "Bill", _
.Address = "555 Center Street, Tacoma, WA 97158", _
.Phone = "555-9999" _
} _
}
Dim newCustList = _
custList.Select( _
Function(c) New With { _
.UCName = c.Name.ToUpper, _
.UCAddress = c.Address.ToUpper _
} _
)
For Each c In newCustList
Console.WriteLine(c.UCName)
Next

Scoping of Anonymous Types

One point that is important to make is that anonymous types (and collections of them) are limited to local scope.  This, of course, should be clear based on the semantics of VB - if you want to return an object from a function, you must declare the function to return an object of that type.  But if you are using an anonymous type, you can't declare the function to return such a type.  Once you want to use an object outside of a function, you must make it a nominal type (a named type).

If you have an object (or collection of objects) of an anonymous type that you want to use outside of a function, the approach to take is to define a nominal type with appropriate scope, and rewrite your code to project your nominal type.

The VB specification states that if you have two instances of an anonymous type such that all of their properties are named the same, the properties are in the same order, and the types of each of the properties are identical, then the two anonymous objects have the same type.  You can use this characteristic of anonymous types to implement some useful idioms in local scope.  For instance, this allows you to create two queries that both result in a collection of anonymous types, and if they are the same, you can concatenate the results.  The following code shows this.

Dim collection1() As Integer = {1, 2, 3}
Dim collection2() As Integer = {4, 5, 6}
Dim q1 = _
From i In collection1 _
Select New With { _
.FromCollection = 1, _
.Value = i _
}
Dim q2 = _
From i In collection2 _
Select New With { _
.FromCollection = 2, _
.Value = i _
}
Dim q3 = q1.Concat(q2)
For Each v In q3
Console.WriteLine(v)
Next

Using the facility that anonymous types have a pretty good ToString method, this example produces the following output:

{ FromCollection = 1, Value = 1 }
{ FromCollection = 1, Value = 2 }
{ FromCollection = 1, Value = 3 }
{ FromCollection = 2, Value = 4 }
{ FromCollection = 2, Value = 5 }
{ FromCollection = 2, Value = 6 }

There are techniques where you can take advantage of knowledge of the compiler implementation to allow anonymous types to escape from local scope.  I've seen such types hanging out in lower downtown Seattle.  They start drinking wine in the morning, smoking crack, and mugging people.  For their own good (and ours), keep them in local scope.

[Table of Contents] [Next Topic] [Blog Map]

Examples.txt

Comments

  • Anonymous
    March 14, 2010
    Tuples, like anonymous types, should be used when you want immutable data containers that don’t define behavior. In addition, only use Tuples for those types that don’t contain important types for your business model. Tuples, because of the naming conventions and the limitations on behavior don’t carry much semantic information, and can’t convey knowledge about your code to other developers.