Internals of tuple literals
My last post about tuple literals gave brief introduction to these. This post goes to internals of tuple literals, peeks behind the compiler and shows what happens with tuple literals internally. This post is for developers who are new to tuple literals and want to gain more deep understanding of these.
Tuple literals after compiling
Let’s write a simple piece of code, compile it and then decompile it without PDB-file available. I’m using this sample class to make an experiment.
public class TupleLiteralsDemo
{
public static (int Id, string Name) GetCustomer()
{
return (1, "Microsoft");
}
public static void Run()
{
var customer = GetCustomer();
Console.WriteLine("Name: " + customer.Name);
Console.ReadKey();
}
}
After compiling, removing PDB-file and opening resulting DLL in JetBrains dotPeek the result is following.
public class TupleLiteralsDemo
{
[return: TupleElementNames(new string[] { "Id", "Name" })]
public static ValueTuple<int, string> GetCustomer()
{
return new ValueTuple<int, string>(1, "Microsoft");
}
public static void Run()
{
Console.WriteLine("Name: " + TupleLiteralsDemo.GetCustomer().Item2);
Console.ReadKey();
}
public TupleLiteralsDemo()
{
base..ctor();
}
}
So, what we have here? The tuple with nice names turns out to be ValueTuple<T> class supported by TupleElementNames attribute.
From this we can make some conclusions:
- Tuple literal names are known to Visual Studio and probably other IDE-s and other editors because of TupleElementNames attribute that is applied to return values of methods that return tuples.
- As tuple element names are just decoration defined in attribute these names are not used elsewhere in compiled code.
- Calling code uses Item1, Item2 etc member names of ValueTuple<T>.
We have now bunch of new things to go through and let’s start with ValueTuple class.
What is ValueTuple?
ValueTuple is similar to Tuple but it is struct and therefore it’s a value type. It’s also mutable and different from tuples ValueTuple holds member values as fields not as properties.
I give here explaining quote by Mads Torgensen (Proposal: Language support for Tuples):
As mentioned, I propose to make tuple types structs rather than classes, so that no allocation penalty is associated with them. They should be as lightweight as possible.
Arguably, structs can end up being more costly, because assignment copies a bigger value. So if they are assigned a lot more than they are created, then structs would be a bad choice.
In their very motivation, though, tuples are ephemeral. You would use them when the parts are more important than the whole. So the common pattern would be to construct, return and immediately deconstruct them. In this situation structs are clearly preferable.
Structs also have a number of other benefits, which will become obvious in the following.
From here we can conclude that ValueTuple is not something we can use and expect to have fantastic performance in all cases. When ValueTuple is not an option we can still go with Tuple or custom class.
Naming of members
Let’s jump now to naming of ValueTuple members. As we saw before then there is TupleElementNames attribute that defines the names of value tuple members. We can also leave out the names of tuple members and have demo class like this.
public class TupleLiteralsDemo
{
public static (int, string Name) GetCustomer()
{
return (1, "Microsoft");
}
public static void Run()
{
var customer = GetCustomer();
Console.WriteLine("Name: " + customer.Name);
Console.ReadKey();
}
}
Or even like shown here,
public class TupleLiteralsDemo
{
public static (int, string) GetCustomer()
{
return (1, "Microsoft");
}
public static void Run()
{
var customer = GetCustomer();
Console.WriteLine("Name: " + customer.Item2);
Console.ReadKey();
}
}
As we didn’t specify names for tuple members then default naming is applied, As there are no custom names then also TupleElementNames attribute is not applied. Decompiiled code is shown here.
public class TupleLiteralsDemo
{
public static ValueTuple<int, string> GetCustomer()
{
return new ValueTuple<int, string>(1, "Microsoft");
}
public static void Run()
{
Console.WriteLine("Name: " + TupleLiteralsDemo.GetCustomer().Item2);
Console.ReadKey();
}
public TupleLiteralsDemo()
{
base..ctor();
}
}
Tuple literal member names are optional and it’s here more for our own convenience when writing code.
Conflicting member names
Let’s now try to strike in to automatic tuple member naming. We leave first element without name and try to name second element as Item1.
public class TupleLiteralsDemo
{
public static (int, string Item1) GetCustomer()
{
return (1, "Microsoft");
}
public static void Run()
{
var customer = GetCustomer();
Console.WriteLine("Name: " + customer.Item2);
Console.ReadKey();
}
}
We get already warning in Visual Studio even without compiling.
When trying to compile we will get the same error by compiler.
Wrapping up
Tuple literals are tuples of value type and they support naming of their members. Although naming happens in Visual Studio like it is some native and built-in langugae feature it is still a little tricking as member names are defined through TupleElementNames attribute that is not made visible in our code. And internally our code uses still default names of members (Item1…ItemN). We were also able define value tuple without any member names and then we got automatically those classic member names. Compiler expects classic member names with indices be defined in certain order and if we try to conflict this rule then error is given.
Pingback:The Morning Brew - Chris Alcock » The Morning Brew #2510
Pingback:Internals of tuple literals - How to Code .NET
Pingback:Newsy .NET 2018-01-28 – DevNation