What's the difference between System.ValueTuple and System.Tuple?
What are
ValueTuples
and why notTuple
instead?
A ValueTuple
is a struct which reflects a tuple, same as the original System.Tuple
class.
The main difference between Tuple
and ValueTuple
are:
System.ValueTuple
is a value type (struct), whileSystem.Tuple
is a reference type (class
). This is meaningful when talking about allocations and GC pressure.System.ValueTuple
isn't only astruct
, it's a mutable one, and one has to be careful when using them as such. Think what happens when a class holds aSystem.ValueTuple
as a field.System.ValueTuple
exposes its items via fields instead of properties.
Until C# 7, using tuples wasn't very convenient. Their field names are Item1
, Item2
, etc, and the language hadn't supplied syntax sugar for them like most other languages do (Python, Scala).
When the .NET language design team decided to incorporate tuples and add syntax sugar to them at the language level an important factor was performance. With ValueTuple
being a value type, you can avoid GC pressure when using them because (as an implementation detail) they'll be allocated on the stack.
Additionally, a struct
gets automatic (shallow) equality semantics by the runtime, where a class
doesn't. Although the design team made sure there will be an even more optimized equality for tuples, hence implemented a custom equality for it.
Here is a paragraph from the design notes of Tuples
:
Struct or Class:
As mentioned, I propose to make tuple types
structs
rather thanclasses
, 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, thenstructs
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.
Examples:
You can easily see that working with System.Tuple
becomes ambiguous very quickly. For example, say we have a method which calculates a sum and a count of a List<Int>
:
public Tuple<int, int> DoStuff(IEnumerable<int> values)
{
var sum = 0;
var count = 0;
foreach (var value in values) { sum += value; count++; }
return new Tuple(sum, count);
}
On the receiving end, we end up with:
Tuple<int, int> result = DoStuff(Enumerable.Range(0, 10));
// What is Item1 and what is Item2?
// Which one is the sum and which is the count?
Console.WriteLine(result.Item1);
Console.WriteLine(result.Item2);
The way you can deconstruct value tuples into named arguments is the real power of the feature:
public (int sum, int count) DoStuff(IEnumerable<int> values)
{
var res = (sum: 0, count: 0);
foreach (var value in values) { res.sum += value; res.count++; }
return res;
}
And on the receiving end:
var result = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {result.sum}, Count: {result.count}");
Or:
var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {sum}, Count: {count}");
Compiler goodies:
If we look under the cover of our previous example, we can see exactly how the compiler is interpreting ValueTuple
when we ask it to deconstruct:
[return: TupleElementNames(new string[] {
"sum",
"count"
})]
public ValueTuple<int, int> DoStuff(IEnumerable<int> values)
{
ValueTuple<int, int> result;
result..ctor(0, 0);
foreach (int current in values)
{
result.Item1 += current;
result.Item2++;
}
return result;
}
public void Foo()
{
ValueTuple<int, int> expr_0E = this.DoStuff(Enumerable.Range(0, 10));
int item = expr_0E.Item1;
int arg_1A_0 = expr_0E.Item2;
}
Internally, the compiled code utilizes Item1
and Item2
, but all of this is abstracted away from us since we work with a decomposed tuple. A tuple with named arguments gets annotated with the TupleElementNamesAttribute
. If we use a single fresh variable instead of decomposing, we get:
public void Foo()
{
ValueTuple<int, int> valueTuple = this.DoStuff(Enumerable.Range(0, 10));
Console.WriteLine(string.Format("Sum: {0}, Count: {1})", valueTuple.Item1, valueTuple.Item2));
}
Note that the compiler still has to make some magic happen (via the attribute) when we debug our application, as it would be odd to see Item1
, Item2
.
What's the difference between System.Tuple and (,)?
(t1,t2)
is a ValueTuple<,>
not a Tuple<,>
So doing the following will work:
ValueTuple<int, int> hey = (1, 2);
However, this will give you a type error
Tuple<int, int> hey = (1, 2);
More information on the difference between ValueTuple
and Tuple
can be found on this question/answers What's the difference between System.ValueTuple and System.Tuple?
Number and type of elements in System.ValueTuple
Yes you can iterate through the Items via the following for
loop:
var tuple = ("First", 2, 3.ToString());
ITuple indexableTuple = (ITuple)tuple;
for (var itemIdx = 0; itemIdx < indexableTuple.Length; itemIdx++)
{
Console.WriteLine(indexableTuple[itemIdx]);
}
- The trick is that you need to explicitly convert your
ValueTuple
toITuple
- That interface exposes
Length
and index operator
ITuple
resides inside the System.Runtime.CompilerServices
namespace.
How does the System.ValueTuple NuGet expand my language features?
The compiler simply needs certain features to exist to bind to them. In the case of (string Name, int Age)
it is looking for System.ValueTuple<T1,T2>
- and as long as it finds that, it is happy. It doesn't usually matter from where - if it comes from the current runtime system library: fine; if it comes from a nuget package: also fine!
This has a lot of pedigree; this same approach applies to a lot of things:
- "LINQ" - just needs the right methods to resolve, whether by extension methods or not (this has allowed "LinqBridge", for example, to retro-fit LINQ onto older runtimes)
- async/await - just needs the
GetAwaiter()
etc methods to exist, whether by extension methods or not - async iterator blocks and async foreach - just needs the
IAsyncEnumerable<T>
etc APIs to exist (and the correct builder APIs for async iterators) - async dispose - just needs the
IAsyncDisposable
interface to exist - etc
so .NET and C# have enjoyed a long history of allowing nuget packages (or other library updates) to "light up" C# features that are designed for "current" frameworks.
Without those methods / types / etc being available, the compiler still knows about the feature - it just can't use it unless it knows how to attach. If the type / method resolves: you're golden.
Predefined type 'System.ValueTuple´2´ is not defined or imported
For .NET 4.6.2 or lower, .NET Core 1.x, and .NET Standard 1.x you need to install the NuGet package System.ValueTuple
:
Install-Package "System.ValueTuple"
Or using a package reference in VS 2017:
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
.NET Framework 4.7, .NET Core 2.0, and .NET Standard 2.0 include these types.
ValueTuple T1, T2 ' exists in both 'System.ValueTuple ...' and 'mscorlib ...'
ASP.NET web pages are compiled at runtime, and this compilation ignores the reference assemblies that VS uses. If .NET 4.7 is available at runtime, all of .NET 4.7's types, including its definition of ValueTuple
, will be picked up along with any other definition you might have from other DLLs.
The easiest ways to avoid this problem if you've already installed .NET 4.7 are either to use .NET 4.7 in your development environment as well, or to avoid runtime compilation.
For the former, you'd need to install the .NET 4.7 targeting pack. It should be an option in the Visual Studio installer. When you do that, you can avoid the ValueTuple
NuGet package and thereby the conflict.
For the latter, you can turn on precompilation when you publish your web project. When you do this, all the compilation that normally happens at runtime instead occurs during the publishing, in a controlled way where .NET 4.7's additions won't be seen.
System.Tuple defined in multiple assemblies
It looks like Raven.Abstractions is a .NET 3.5 assembly, instead of a .NET 4.0 reference. If you look at the source code for RavenDB Here, you'll notice that it defines a Tuple<T, U>
class if it's being built for .NET 3.5.
You must have updated your project from .NET 3.5 to .NET 4.0 at some point, because I also notice you have a reference to System.Core. In .NET 4, System.Core is reference by default and so that reference in your project is redundant.
My suggestion is you find all assemblies that you are already referencing in your project and replace them with .NET 4 versions, if available. I know that .NET has that extra compatibility, in that it allows two versions of the runtime to be loaded in a process at the same time, but I can't imagine that it doesn't affect performance on some level, even if only a tiny bit. The .NET 4 runtime is superior in a lot of ways though, specifically the large object heap. I guess I'm ranting a little bit. Just update what assemblies you can to 4.0, or change your project's version back down to 3.5.
Related Topics
Unzip Files Programmatically in .Net
How to Parse Hex Values into a Uint
Comparing Two Strings, Ignoring Case in C#
C#: Raising an Inherited Event
Linq to Entities Does Not Recognize the Method Last. Really
How to Implement Ienumerable<T>
C#: List All Classes in Assembly
Good Gethashcode() Override for List of Foo Objects Respecting the Order
Large Object Heap Fragmentation
Generating Dll Assembly Dynamically at Run Time
Xdocument Containing Namespaces
Does .Net Have a Way to Check If List a Contains All Items in List B
Getfiles with Multiple Extensions