With new c# 7 tuple syntax, is it possible to specify a lambda with a tuple as parameter and use unpacked values inside the lambda?
Example:
var list = new List<(int,int)>();
normal way to use a tuple in lambda:
list.Select(value => value.Item1*2 + value.Item2/2);
i expected some new sugar to avoid .Item1
.Item2
, like:
list.Select((x,y) => x*2 + y/2);
The last line does not work because it is treated as two parameters for lambda. I am not sure if there is a way to do it actually.
EDIT:
I tried double parentesis in lambda definition and it didn't work: ((x,y)) => ...
, and maybe it was stupid to try, but double parenthesis actually work here:
list.Add((1,2));
Also, my question is not quite about avoiding ugly default names .Item .Item2
, it is about actual unpacking a tuple in lambda (and maybe why it's not implemented or not possible). If you came here for a solution to default names, read Sergey Berezovskiy's answer.
EDIT 2:
Just thought of a more general use case: is it possible (or why not) to "deconstruct" tuple passed to a method? Like this:
void Foo((int,int)(x,y)) { x+y; }
Instead of this:
void Foo((int x,int y) value) { value.x+value.y }
As you have observed, for:
var list = new List<(int,int)>();
One would at least expect to be able to do the following:
list.Select((x,y) => x*2 + y/2);
But the C# 7 compiler doesn't (yet) support this. It is also reasonable to desire sugar that would allow the following:
void Foo(int x, int y) => ...
Foo(list[0]);
with the compiler converting Foo(list[0]);
to Foo(list[0].Item1, list[0].Item2);
automatically.
Neither of these is currently possible. However, the issue, Proposal: Tuple deconstruction in lambda argument list, exists on the dotnet/csharplang
repo on GitHub, requesting that the language team consider these features for a future version of C#. Please do add your voices to that thread if you too would like to see support for this.
You should specify names of tuple properties (well, ValueTuple have fields) otherwise default names will be used, as you have seen:
var list = new List<(int x, int y)>();
Now tuple have nicely named fields which you can use
list.Select(t => t.x * 2 + t.y / 2)
Don't forget to add System.ValueTuple package from NuGet and keep in mind that ValueTuples are mutable structs.
Update: Deconstruction currently represented only as an assignment to existing variables (deconstructon-assignment) or to newly created local variables (deconstruction-declaration). Applicable function member selection algorithm is the same as before:
Each argument in argument list corresponds to a parameter in the function member declaration as described in §7.5.1.1, and any parameter to which no argument corresponds is an optional parameter.
Tuple variable is a single argument. It cannot correspond to several parameters in the formal parameters list of the method.
Deconstructions in C# 7.0 support three forms:
(var x, var y) = e;
), (x, y) = e;
),foreach(var(x, y) in e) ...
).Other contexts were considered, but likely have decreasing utility and we couldn't complete them in the C# 7.0 timeframe. Deconstruction in a let clause (let (x, y) = e ...
) and in lambdas seem good candidates for future expansion.
The latter is being discussed in https://github.com/dotnet/csharplang/issues/258
Do voice your feedback and interest there, as it will help champion proposals.
More details on what was included in C# 7.0 deconstruction in the design doc.
The problem you're running into is the inability of the compiler to infer the type in this expression:
list.Select(((int x, int y) t) => t.x * 2 + t.y / 2);
But since (int, int)
and (int x, int y)
are the same CLR type (System.ValueType<int, int>
), if you specify the type parameters:
list.Select<(int x, int y), int>(t => t.x * 2 + t.y / 2);
It will work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With