C # 7元组和兰姆达

使用新的 c # 7 tuple 语法,是否可以指定一个带有 tuple 作为参数的 lambda,并在 lambda 中使用未打包的值?

例如:

var list = new List<(int,int)>();

在 lambda 中使用元组的正常方法:

list.Select(value => value.Item1*2 + value.Item2/2);

我期望一些新的糖,以避免 .Item1 .Item2,如:

list.Select((x,y) => x*2 + y/2);

最后一行不起作用,因为它被视为 lambda 的两个参数。我不确定是否真的有办法做到这一点。

编辑:

我在 lambda 定义中尝试了双括号,但是没有成功: ((x,y)) => ...,也许这样做很愚蠢,但是双括号在这里确实有效:

list.Add((1,2));

另外,我的问题并不完全是关于避免使用丑陋的默认名称 .Item .Item2,而是关于在 lambda 中实际解压缩一个元组(也许还有为什么没有实现或不可能实现)。如果您来这里是为了获得默认名称的解决方案,请阅读 谢尔盖 · 别列佐夫斯基的回答

编辑2:

只是想到一个更通用的用例: 是否有可能(或者为什么没有)“解构”传递给方法的元组?像这样:

void Foo((int,int)(x,y)) { x+y; }

而不是这样:

void Foo((int x,int y) value) { value.x+value.y }
23742 次浏览

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.

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.

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.

Deconstructions in C# 7.0 support three forms:

  • deconstruction-declaration (like (var x, var y) = e;),
  • deconstruction-assignment (like (x, y) = e;),
  • and deconstruction-foreach (like 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.