A few weeks ago at Connect(); Microsoft announced the availability of Visual Studio 2017 RC and with it comes C# 7.0. With C# 7.0, the language designers were focused on three areas:
- Data consumption
- Code simplification
- Performance
Over the next few blog posts we'll have a look at what I think are some of the more interesting additions.
Tuples
A tuple is defined by MSDN as:
A tuple is a data structure that has a specific number and sequence of elements. An example of a tuple is a data structure with three elements (known as a 3-tuple or triple) that is used to store an identifier such as a person's name in the first element, a year in the second element, and the person's income for that year in the third element.
Prior to C# 7.0, you could utilize out parameters, System.Tuple<T>, custom types or dynamic types to return multiple values (a tuple) from a method. Let's review each of these options.
Much like the ref keyword, the out keyword causes arguments being passed to a method to be passed by reference. The key difference is that the ref keyword requires the argument to be initialized prior to invoking the method. In contrast, the out keyword forces the author of the method to assign a value to the argument before control leaves the method.
Seeing as how the author must assign a value to any out parameter before control leaves the method, out parameters can be used to return multiple values from a method. Below is an example of utilizing out parameters.
public static void GetUser(out int age, out string name)
{
age = 35;
name = "Joe";
}
While this works it's tends to be awkward to author and consume.
You can always just return an object as more or less a property bag or POCO holding the values to be returned. The BCL gives us a built-in tuple type in the System namespace conveniently named Tuple. By way of generics, the BCL offers a few flavors of this type allowing you to specify the number of values and the types of the values to be returned (i.e. Tuple<T1, T2, T3...>) Below is an example of utilizing System.Tuple<T1, T2>.
public static System.Tuple<int, string> GetUser()
{
return new System.Tuple<int, string>(35, "Joe");
}
var user = GetUser();
var age = user.Item1;
var name = user.Item2;
I find using System.Tuple<T> as the return type straightforward to author but much more ambiguous to consume. In the example above, how is the caller of GetUser() suppose to know what Item1 and Item2 of the returned tuple represent? Furthermore, this requires memory allocation of the tuple object. Also notice how verbose this code is to write and consume.
Custom return types
When in doubt, you can always author your own type containing as many values as you like to be returned.
public struct User
{
public int Age {get;set;}
public string Name {get;set;}
}
public static User GetUser()
{
return new User { Age = 35, Name = "Joe" };
}
var user = GetUser();
var age = user.Age;
var name = user.Name;
The code above is a lot more explicit in it's intent but much more verbose. While I love the explicitness of it, writing code like this can lead to an explosion of code that will need to be maintained and tested.
The dynamic type bypasses compile-time type checking. What this enables us to do is to return an object as a property bag but with loosely defined properties. Again below is an example.
public static dynamic GetUser()
{
return new { Age = 35, Name = "Joe" };
}
var user = GetUser();
var age = user.Age;
var name = user.Name;
From my perspective, the code above is the same as using System.Tuple with the added bonus of throwing out static typing. I don't want to get into a debate over statically typed vs dynamically typed languages but the reason I see this as a shortcoming in this instance is because you could have gotten the type checking at compile time if you had just used System.Tuple.
Ok with that out of the way, let's talk about the new features added to C# 7 starting with tuple types and literals.
Tuple Types and Literals
Tuple types are a way to declare a tuple inline. You might be asking yourself "how is that any different than using System.Tuple<T>?" Well, in this scheme, tuples are value types and the elements making up a tuple are public fields. What this also means is that you can use the equality operator (==) to compare two tuples. Two tuples are equal if all their matching elements are equal and have the same hash code. Ok let's look at some code.
public static (int, string) GetUser()
{
return (35, "Joe");
}
var user = GetUser();
var age = user.Item1;
var name = user.Item2;
The GetUser() method returns a tuple type that is defined as two fields, an int and a string. Again, you may be asking that same questions about how this better than using System.Tuple<int, string> because you still have little context of what the int and string represent in terms of the User. Here's where tuple types get really interesting, you can optionally add names to the fields. In other words, Item1, Item2, Item3... are just default names given to the fields in absence of their declaration.
public static (int age, string name) GetUser()
{
return (35, "Joe");
}
var user = GetUser();
var age = user.age;
var name = user.name;
Conclusion
I feel as if the language designers really hit a homerun in terms of the simplicity of declaring a tuple. Given how terse the declaration is, I think it will make data consumption scenario's such as utilizing a SOAP or REST API much easier. Also, because of value equality, you could use a tuple in situations such as needing a multifactor key for a dictionary.
Update
A Redditor by the name of silvenga asked the following question about using tuple types in an Entity Framework projection:
Random thought - would EF 6 be able to project to these new tuples?
It's an interesting idea so I figured I'd give it a shot. I fired Visual Studio up and tried the following:
using (var context = new Model())
{
var app = context.Users.Select(c => (name: c.Name, id: c.Id)).Single();
}
It turns out you get the following compiler error:
CS8143 C# An expression tree may not contain a tuple literal.
I also tried this:
using (var context = new Model())
{
var app = context.Users.Select(c => new ValueTuple<string, int>(c.Name, c.Id)).Single();
}
Which resulted in the following runtime error:
System.ArgumentException: 'Cannot resolve method Void .ctor(System.String, Int32) because the declaring type of the method handle System.ValueTuple`2[T1,T2] is generic. Explicitly provide the declaring type to GetMethodFromHandle. '
I did a bit of googling and I can't seem to find a way around this error so for now it looks as if there isn't a way of using tuple types within a LINQ projection.
Full disclosure, I'm using Visual Studio 2017 RC (Version 15.0.26020.0), Entity Framework (Version 6.1.3), the System.ValueTuple Nuget package (Version 4.3.0) and the 4.6.2 framework.