Sdílet prostřednictvím


K is for… Key

k

‘Key’ is a rather overloaded term in .NET and programming circles in general. There are database primary and foreign keys, hash keys, keys to styles and templates in WPF, registry keys, key codes for each key on the keyboard, and many others.  For this post, I’m going to touch on the concept of Key properties in anonymous types.

First though, what’s an anonymous type?  Anonymous types were introduced in Visual Studio 2008 with C# 3.0 and Visual Basic 9 and figure prominently when using LINQ (Language Integrated Query).

In C#, anonymous types make use of a new keyword var which indicates the data type of the following variable reference is an anonymous type.  Note, an anonymous type is not synonymous with a variant or late-bound type. Variables defined this way are strongly-typed; it’s just that the CLR is generating the type name and definition for you.

For example, here’s a complete (albeit simplistic) program that uses an anonymous type representing a city.  On lines 16 and 17 you can see that the type can be used just as any other explicitly defined type would be; it just doesn’t have a name that you yourself have defined.

    1:  using System;
    2:   
    3:  namespace ConsoleCS
    4:  {
    5:      class Program
    6:      {
    7:          static void Main(string[] args)
    8:          {
    9:              var city1 = new
   10:              {
   11:                  Name = "Boston",
   12:                  State = "MA",
   13:                  Zip = "02134"
   14:              };
   15:   
   16:              Console.WriteLine("{0}, {1} {2}", 
   17:                  city1.Name, city1.State, city1.Zip);
   18:              Console.ReadLine();
   19:          }
   20:      }
   21:  }

In this particular case, a call to Console.WriteLine(city1.GetType().ToString()) yields the following as the compiler-defined type name:

<>f__AnonymousType0`3[System.String,System.String,System.String]

The equivalent in Visual Basic would be:

    1:  Module ConsoleVB
    2:      Sub Main()
    3:          Dim city1 = New With { _
    4:                  .Name = "Boston", _
    5:                  .State = "MA", _
    6:                  .Zip = "02134"}
    7:   
    8:          Console.WriteLine("{0}, {1} {2}", _
    9:              city1.Name, city1.State, city1.Zip)
   10:          Console.ReadLine()
   11:      End Sub
   12:  End Module

Note, that in Visual Basic we reuse the Dim keyword for declaring anonymous types and introduce the With keyword, but the rest of the syntax is more or less identical to C#.  The compiler-generated type name in this case is

VB$AnonymousType_0`3[System.String,System.String,System.String]

So what’s all this got to do with the Key keyword?  Well, it turns out that anonymous types in C# and Visual Basic have different behaviors, and the Key keyword is, well, key in differentiating those behaviors.

In Visual Basic, the following code snippet would result in ‘false’ being output on the console window:

    1:  Dim city1 = New With { _
    2:      .Name = "Boston", _
    3:      .State = "MA", _
    4:      .Zip = "02134"}
    5:  Dim city2 = New With { _
    6:      .Name = "Boston", _
    7:      .State = "MA", _
    8:      .Zip = "02134"}
    9:  Console.WriteLine(city1.Equals(city2))

In contrast, the analogous code in C# would yield ‘true’, since the Equals method for the Name, State, and Zip properties each return ‘true.’

Now, let’s add Key to some of the properties of the Visual Basic anonymous class definitions:

    1:  Dim city1 = New With { _
    2:       Key .Name = "Boston", _
    3:       Key .State = "MA", _
    4:           .Zip = "02134"}
    5:  Dim city2 = New With { _
    6:       Key .Name = "Boston", _
    7:       Key .State = "MA", _
    8:           .Zip = "02134"}
    9:  Console.WriteLine(city1.Equals(city2))

Now, city1 does equal city2 because the equality test in Visual Basic compares those fields marked with Key.  In fact, only the fields marked with Key are compared, so the following snippet also results in equality of the two objects, despite the fact that the zip code for city1 was modified in line 9 and differs from that of city2.

 
    1:  Dim city1 = New With { _
    2:       Key .Name = "Boston", _
    3:       Key .State = "MA", _
    4:           .Zip = "02134"}
    5:  Dim city2 = New With { _
    6:       Key .Name = "Boston", _
    7:       Key .State = "MA", _
    8:           .Zip = "02134"}
    9:  city1.Zip = "90210"
   10:  Console.WriteLine(city1.Equals(city2))

By the way, did you notice the property is mutable in Line 9?  This is another difference between Visual Basic and C#’s handling of anonymous types.  In C#, all fields of an anonymous type are read-only; in Visual Basic only fields marked with Key are read-only.

If you want to dig a little further into the topic of anonymous types and recommendations of when to use them (or not use them), here’s a few links that will come in handy: