C#7元组和Lambda


75

使用新的C#7元组语法,是否可以指定一个以元组为参数的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中实际拆开元组(以及为什么不能实现或不可能)。如果您是来这里使用默认名称的解决方案,请阅读Sergey Berezovskiy的答案

编辑2:

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

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

代替这个:

void Foo((int x,int y) value) { value.x+value.y }

Answers:


53

如您所见,对于:

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

人们至少希望能够做到以下几点:

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

但是C#7编译器(尚)不支持此功能。要求糖允许以下行为也是合理的:

void Foo(int x, int y) => ...

Foo(list[0]);

编译器自动转换Foo(list[0]);Foo(list[0].Item1, list[0].Item2);

目前,这两种方法都不可行。但是,GitHub上的仓库中存在“提案:lambda参数列表中的元组解构”问题,dotnet/csharplang要求语言团队考虑将这些功能用于将来的C#版本。如果您也希望看到对此的支持,请不要在该主题中添加您的声音。


41

您应该指定元组属性的名称(好,ValueTuple有字段),否则将使用默认名称,如您所见:

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

现在,元组具有命名良好的字段,您可以使用

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

不要忘记从NuGet添加System.ValueTuple包,并记住ValueTuples是可变结构。


更新:解构当前仅表示为对现有变量的分配(解构分配)或对新创建的局部变量(解构声明)。适用的函数成员选择算法与以前相同:

参数列表中的每个参数都对应于第7.5.1.1节中描述的函数成员声明中的参数,并且任何不与参数对应的参数都是可选参数。

元组变量是单个参数。它不能对应于该方法的形式参数列表中的几个参数。


很好,但这只是重命名,值仍然是传递给lambda的单个对象的字段。我很好奇是否有一种方法可以将其分解为多个变量。
拉斯特

1
@Rast:据我所知,对元组的解构仅在您的变量声明可以包含来自元组的赋值的情况下才有效。这将排除参数列表。谢尔盖的例子似乎很干净。有什么不适合您的需求?
彼得·杜尼奥

2
@Peter Duniho这只是我的好奇心。我认为解构之类的方法在这种情况下可能有用。
拉斯特

@RastEnumerable.Select有一个Func<TSource, TResult>委托作为参数。它接受TSource类型的单个参数,您不能更改它
Sergey Berezovskiy

1
@Rast:也许但不在参数列表中,在该列表中不允许分配非默认值。相反list.Select(t => { (int x, int y) = t; return x * 2 + y / 2 });,您可以执行类似的操作,但是到那时,我认为您会失去元组语法的简洁性,而不是上面的选择。
彼得·杜尼奥

12

C#7.0中的解构支持三种形式:

  • 解构声明(例如(var x, var y) = e;),
  • 解构分配(如(x, y) = e;),
  • 和deconstruction-foreach(如foreach(var(x, y) in e) ...)。

考虑了其他上下文,但实用性可能会下降,我们无法在C#7.0时间范围内完成它们。在let子句(let (x, y) = e ...)和lambda中解构似乎是将来扩展的不错选择。

后者正在https://github.com/dotnet/csharplang/issues/258中讨论

在这里表达您的反馈和兴趣,因为这将有助于提出建议。

设计文档中有关C#7.0解构中包含的内容的更多详细信息。


4

您正在运行的程序是编译器无法在此表达式中推断类型的:

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

但是,由于(int, int)(int x, int y)是相同的CLR类型(System.ValueType<int, int>),因此如果您指定类型参数:

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

它将起作用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.