问题
I'm surprised there isn't a question on this already. C# 7 added value tuples. I'm trying to figure out when I should adopt these features.
Take this dictionary for example uses Anonymous Type:
var changesTypeMapByEntityState = this.ChangeTracker.Entries()
.Where(x => (int)x.State > (int)EntityState.Unchanged)
.GroupBy(x => new { Type = x.Entity.GetType(), x.State })
.ToDictionary(x => x.Key, x => x.ToList());
Vs this Dictionary which uses Value Tuples
var changesTypeMapByEntityState = this.ChangeTracker.Entries()
.Where(x => (int)x.State > (int)EntityState.Unchanged)
.GroupBy(x => (Type: x.Entity.GetType(), x.State ))
.ToDictionary(x => x.Key, x => x.ToList());
Which one of these would perform better, and what are the benefits of using the new syntax vs the old?
回答1:
In this situation, there's not much difference.
But in other situations value tuples can have a significant performance advantage. Because they are value types rather than reference types, a careful programmer can sometimes use them to avoid allocating new memory on the heap that must also be managed and collected. Additionally, value tuples are easier to share outside of the local scope, and so are legal in a number of situations where anonymous types are not.
That said, value types and reference types can also have different semantics, meaning you may have situations where a reference to anonymous type is much more appropriate, especially if you copy the reference around a lot.
Finally, it's not common for GC memory management to be the main performance driver of a program. The value tuple performance advantages aren't very likely to make a big enough difference to rush off and change all your old code, unless you have unlimited time to spend with a profiler tool to be sure it's a win. It's more worthwhile to be mindful of which choice produces clearer code or uses better semantics.
回答2:
There's a really obvious case where you'd use a Value Tuple
instead of an anonymous objects, and that's when you need to return objects to the caller.
With the Value Tuple
you have a fast way to return any number of named properties, which is not very feasible with anonymous objects.
For example:
public (int Count, string Hello) GetDataTuple()
{
return (1, "world");
}
public object GetDataObject()
{
return new { Count = 1, Hello = "World" };
}
Then:
var dataTuple = GetDataTuple();
Console.WriteLine(dataTuple.Count); // valid
var dataObject = GetDataObject();
Console.WriteLine(dataOjbect.Hello); // invalid
This also applies, logically, to properties/fields in classes:
class Test
{
public (int Count, string Hello) DataTuple { get; set; } // valid
public 'A DataObject { get; set; } // obviously invalid
}
回答3:
I’d say the basic advantage is that tuples have names so to speak; a method/property/field can be typed as a tuple.
You can’t have anonymous fields or properties (returning an anonymous type, although feasible, is not straightforward and has uncomfortable limitations).
Another important difference is that anonymous types are reference types while value tuples are value types. This is an important distinction even in scenarios where we are only addressing implicitly typed locals where usage is essentially the same as your example demonstrates.
回答4:
I was doing some research and I found a nice benchmark. The Anonymous types out preformed the value tuples. But this is just one of many test that needs to be run to make an informed decision. I'm only providing this benchmark as convenience so no one else has to look this up. But this does not account for lookups or GC Collect
var valueTupleQuery = from i in Enumerable.Range(0, 100000)
select (a: i, b: i, c: i, d: i, e: i, x: i, y: i + 1, z: i + 2) into x
where x.x > 100001
select (x: x, _: 0) into t
where t.x.x < 0
select t.x;
var anonymousQuery = from i in Enumerable.Range(0, 100000)
select new { a = i, b = i, c = i, d = i, e = i, x = i, y = i + 1, z = i + 2 } into x
where x.x > 100001
select (x: x, _: 0) into t
where t.x.x < 0
select t.x;
var stopwatch = new Stopwatch();
stopwatch.Restart();
for (var i = 0; i < 1000; i++)
{
valueTupleQuery.ToArray();
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
stopwatch.Restart();
for (var i = 0; i < 1000; i++)
{
anonymousQuery.ToArray();
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
Ran on lenovo y700
The ValueTuples took: 3426 MS
Anonymous Types took: 3137 MS
NOTE
In case anyone was wondering I decided to avoid the key all together and just nests my dictionaries
var changesTypeMapByEntityState = this.ChangeTracker.Entries()
.Where(x => (int)x.State > (int)EntityState.Unchanged)
.GroupBy(x => x.Entity.GetType())
.ToDictionary(x => x.Key,
x => x.GroupBy(g => g.State)
.ToDictionary(k => k.Key, v => v.ToList()));
来源:https://stackoverflow.com/questions/48118187/value-tuples-vs-anonymous-types-performance