Skip to main content

...

....

C# - The Tuples and Anonymous Types [Beginner]


Many times when you talk with developers about anonymous types or tuples you will often get a negative gut-reaction. There's probably a bit of that hard-core object-oriented designer in all of us that cringes a bit inside at the idea of using one of these "lightweight" types instead of building a full-fledged class.

But, when used properly, these types can actually improve the maintainability of the code-base when used judiciously because they eliminate the need of creating dozens of one-use, simple types that can simply clutter up a project.

In addition, as we will see, these types have a few tricks up their sleeves that can actually, in some circumstances, make them safer than constructing your own type.

 

Tuples

The Tuple is a family of generic types that hold specific numbers of items of specific types in a specific order. For example:
var triple = new Tuple<int, string, int>(13, "Hello", 42);
 
The tuple above contains exactly three items: an int, followed by a string, followed by an int. Also notice that the values must be passed in at construction. This is because the Tuple is immutable, and once the properties are set, they cannot be changed.

To access the members of a Tuple, you use the properties named Item1, Item2, ... Item7 where N is the position of the item in the constructor arguments.
Console.WriteLine("The first item is: " + triple.Item1);
Console.WriteLine("The second item is: " + triple.Item2);
Console.WriteLine("The third item is: " + triple.Item3);

 

Tuples of Various Sizes

The Tuple is a generic type that can support simple tuples from 1 to 7 generic type arguments:
// a singleton tuple of integer
Tuple<int> x;

// or more
Tuple<int, double> y;

// up to seven
Tuple<int, double, char, double, int, string, uint> z;
 
Now, you may think that a Tuple of 1 is pretty silly, but the main reason it exists is to support tuples beyond size 7, where the tuples become nested. That is, if you want a Tuple beyond size 7, you have to make the 8th item a nested Tuple:
// an 8-tuple
Tuple<int, int, int, int, int, double, char, Tuple<string>> t8;

// an 9-tuple
Tuple<int, int, int, int, double, int, char, Tuple<string, DateTime>> t9;

// a 16-tuple
Tuple<int, int, int, int, int, int, int, Tuple<int, int, int, int, int, int, int, Tuple<int,int>>> t16;
 
Once constructed, you access the properties as with a simple Tuple but with one twist: the nested Tuple is accessed using the Rest property, and then you start over again with Item1...Item7 again:
// To access item 8 of t8
var eightItem = t8.Rest.Item1;
As you can see, dealing with large tuples can quickly become cumbersome and difficult to read, so I generally don't recommend tuples much beyond the 4 item range. 

 

Tuples Simplify Treating Items as Unit

Probably the most powerful feature of tuples is that they let you combine several elements into one unit that can be treated as a whole. This makes it easy to return multiple values from a method (without resorting to out parameters) or to pass multiple values into a method where only one parameter is allowed. But where they really shine, is treating the items together as a combined key:
// Dictionary of Order keyed on ID and Region
var ordersByIdAndRegion = new Dictionary<Tuplegt;int, string>>();
 
Or as the key in a GroupBy() clause in LINQ:
// Group all orders by ID and Region
var groupedOrders = orders.GroupBy(o => Tuple.Create(o.ID, o.Region));
 
Notice that again we could have created our own class here, but because hashing collections like Dictionary require a valid implementation of GetHashCode() and Equals(), you'd have to create your own and further, keep it well maintained:
public class IdAndRegionKey {
    public int Id { get; set; }
    public string Region { get; set; }

    public override bool Equals(object other) {
        var otherKey = other as IdAndRegionKey;

        return otherKey != null 
            ? otherKey.Id == Id && otherKey.Region == Region
            : false;
    }

    public override int GetHashCode() {
        int hash = 23;
        hash += 17 * hash + (Region ?? string.Empty).GetHashCode();
        hash += 17 * hash + Id.GetHashCode();
    }
}
 
Notice how much work we had to do to build such a simple type to be a combination key for our Dictionary! First of all, this is a lot of typing for something that has such a small scope. Second, it's harder to maintain because you'd have to make sure to implement (and keep updated) the Equals() and GetHashCode() method. 

Sure, you could do something like the code above, but why go through the trouble for such a small-scoped type when Tuple will do it for you.

Tuple Factory Methods

So, now you've seen how to explicitly create a Tuple using new, such as this statement where we explicitly state the Tuple will contain an int and a string:
var double = new Tuple<int, string>(42, "The answer");
 
But there is another way! There is a static, non-generic class named Tuple that provides factory methods so that you can construct tuples using type inference:
var double = Tuple.Create(42, "The answer");
 
So the factory method above determines at compile-time that 42 is an int and "The answer" is a string and constructs the appropriate Tuple to house it. This will work for all the way up to octuples.

Tuple in a Nutshell

As you can see, there is great power (and potential for great abuse) in a Tuple. The Tuple allows you to quickly construct a type that contains several items and treats them as a unit. These instances are immutable and can be constructed either directly or via factory methods in the Tuple static class. Finally, the Tuple already has an appropriate GetHashCode() and Equals() method overrides, which allows you to use it safely in LINQ or as a key to a hashing collection such as Dictionary or HashSet.

On the downside, the Item1, Item2, etc. property names are not very descriptive and large tuples can quickly become difficult to maintain if used explicitly.

Anonymous Types

Anonymous types are similar to Tuple yet very different. Both of these types are meant to attack the problem of producing quick and safe types of limited scope, but each attacks the problem differently.

The Tuple gives you, for all intents and purposes, a well-defined type that can be returned from methods and thus returned from methods, used in other files, etc. But in consequence, it is defined extremely generically by the types it contains and no more than that. This means that every Tuple looks the same to .NET regardless of whether the first is a pair of order IDs and the second is a pair of days of the month.

Anonymous types, on the other hand, define a type at the point of creation that is much more descriptive. Where a Tuple has only a very generic set of Item... properties, the anonymous type has a well defined name and order to its properties.

Creatin an Anonymous Type

Anonymous types are constructed using the object initializer syntax. The main differences being that the type-name is omitted and that the properties must be assigned a value with an inferrable type.

For example, we could use anonymous typing to define a few points:
// same names, types, and order
var point1 = new { X = 13, Y = 7 };
var point2 = new { X = 5, Y = 0 };
 
Notice that we name each property and assign it a value, and that we can infer the type of the property from the value. We can then access these fields by name:
Console.WriteLine("My point is: ({0},{1})", point1.X, point1.Y);
 

The Type Definition

Notice that the two points above have the same property names, same types, and in the same order. This means that (for the most part) they are considered the same type. But change the property name, type, or order, and the types will be considered different:
var point3 = new { Y = 3, X = 5 };             // different order
var point4 = new { X = 3, Y = 5.0 };           // different type for Y
var point5 = new {MyX = 3, MyY = 5 };          // different names
var point6 = new { X = 1, Y = 2, Z = 3 };      // different count
 
Also, remember that we said that the type must be inferable from the assignment, this means that the following expressions are not allowed:
// Null can't be used directly.  Null reference of what type?
var cantUseNull = new { Value = null };
 
In the code above, null could be any reference type, so the anonymous type definition won't work. Thus, in cases like this, you could make it work if you cast null to for an explicit type:
// You can use null if you cast to a type
var canUseNullWithCast = new { Value = (string) null };
 
Now, the type can be inferred and the anonymous type is happy. Speaking of inferrence, you can even infer the property names in many cases. Obviously, this won't work if you are assigning a literal, but if you are assigning a property from a named variable or other property and don't specify a new property name, it will take on the old name by default:
int variable = 42;

// creates two properties named varriable and Now
var implicitProperties = new { variable, DateTime.Now };
 
In the example above, we've created an anonymous type that has a property named variable and a property named Now. However, if you were trying to do this with 42 directly:
var fail = new { 42, DateTime.Now };
 
It would fail at compile time because 42 is a literal and not a named identifier. Similarly, you can't use expressions such as variable + x since that is an expression and not a named identifier.

The Built-in Overrides on Anonymous Types

Once again, like Tuple anonymous types have overridden the Equals() and GetHashCode() for us, which lets us use them in GroupBy() expressions and other similar situations. This again makes them a great choice for grouping together items for a complex key in a LINQ expression:
// group the transactions based on an anonymous type with properties UserId and Date:
byUserAndDay = transactions
        .GroupBy(tx => new { tx.UserId, tx.At.Date })
        .OrderBy(grp => grp.Key.Date)
        .ThenBy(grp => grp.Key.UserId);
 
In fact, this can be much nicer because the Key of the grouping will have the UserId and Date property names, which is much easier to read than Item1 and Item2:
foreach (var group in byUserAndDay)
  {
      // the group’s Key is an instance of our anonymous type
      Console.WriteLine("{0} on {1:MM/dd/yyyy} did:", group.Key.UserId, group.Key.Date);

      // each grouping contains a sequence of the items.
      foreach (var tx in group)
      {
          Console.WriteLine("\t{0}", tx.Amount);
      }
  }
 
One thing we lose with anonymous types is the ability to easily use it beyond the declared scope. That is, if you declare an anonymous type inside a method, it can be difficult to return it as a result or pass it to a new method without type inference. 

Consider, What if you wanted to return the results of the grouping above from a method? How would you define the return type?
Dictionary<???, Order> orders = ...;
 
You see the dilemma? How would you pass an anonymous type outside of a method it is used in? Sure, you could pass it out as object but then it would be difficult to cast back (though you could use dynamic to access the properties again of course).

The Anonymous Types in a Nutshell

Anonymous types give you a lot of power to define robust, short-lived types at the point they are required. These instances are immutable and have more meaningful property names than the Tuple which can make them easier to consume. Like the Tuple, anonymous types have complete overrides for GetHashCode() and Equals() which makes them suitable for hashing and equality checks. 

The main downfall of anonymous types is that they are not intended for use beyond their immediate scope, which means that in cases where you want to return the results of a query from a method, you'd probably want to define a real class.

Summary

Both tuples and anonymous types have their uses. Favor Tuples when the type you need a small type that must live beyond its immediate scope, but for which a full class is overkill. Such uses could be passing a pair of items into a thread start method or into a state variable, or perhaps using them as a combined key for a dictionary.

Anonymous types, on the other hand, are perfect for those cases where you want a very short-lived type (no more than the current scope) to be defined with meaningful names. 

Do not think that you should always favor one or the other, consider what each excels at and choose them accordingly.

Comments

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as

C# WPF Tutorial - Implementing IScrollInfo [Advanced]

The ScrollViewer in WPF is pretty handy (and quite flexible) - especially when compared to what you had to work with in WinForms ( ScrollableControl ). 98% of the time, I can make the ScrollViewer do what I need it to for the given situation. Those other 2 percent, though, can get kind of hairy. Fortunately, WPF provides the IScrollInfo interface - which is what we will be talking about today. So what is IScrollInfo ? Well, it is a way to take over the logic behind scrolling, while still maintaining the look and feel of the standard ScrollViewer . Now, first off, why in the world would we want to do that? To answer that question, I'm going to take a an example from a tutorial that is over a year old now - Creating a Custom Panel Control . In that tutorial, we created our own custom WPF panel (that animated!). One of the issues with that panel though (and the WPF WrapPanel in general) is that you have to disable the horizontal scrollbar if you put the panel in a ScrollV