Подтвердить что ты не робот

С# 7 кортежей и лямбда

С новым синтаксисом кортежей С# 7 можно ли указать лямбда с кортежем в качестве параметра и использовать распакованные значения внутри лямбда?

Пример:

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

обычный способ использования кортежа в лямбда:

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

Я ожидал появления нового сахара, чтобы избежать .Item1 .Item2, например:

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

Последняя строка не работает, потому что она рассматривается как два параметра для лямбда. Я не уверен, есть ли способ сделать это на самом деле.

EDIT:

Я попробовал двойной родительский учет в определении лямбда, и он не работал: ((x,y)) => ..., и, возможно, было глупо пытаться, но двойная скобка действительно работает здесь:

list.Add((1,2));

Кроме того, мой вопрос не совсем о том, чтобы избежать уродливых имен по умолчанию .Item .Item2, речь идет о фактической распаковке кортежа в лямбда (и, возможно, почему это не реализовано или невозможно). Если вы пришли сюда для решения имен по умолчанию, прочитайте ответ Сергея Березовского.

ИЗМЕНИТЬ 2:

Просто подумал о более общем случае: возможно ли (или почему нет) "деконструировать" кортеж, переданный методу? Вот так:

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

Вместо этого:

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

Ответ 1

Как вы заметили, для:

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

Можно было бы, по крайней мере, ожидать, что вы сможете сделать следующее:

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

Но компилятор С# 7 (пока) не поддерживает это. Также разумно желать сахара, который позволил бы:

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

Foo(list[0]);

с автоматическим преобразованием компилятора Foo(list[0]); в Foo(list[0].Item1, list[0].Item2);.

В настоящее время ни один из них не является возможным. Тем не менее, проблема Предложение: Деконструкция набора строк в списке аргументов лямбда существует в ретрансляции dotnet/csharplang на GitHub, требуя, чтобы языковая команда рассматривала эти функции для будущей версии С#. Пожалуйста, добавьте свои голоса в этот поток, если вы тоже захотите увидеть поддержку этого.

Ответ 2

Вы должны указать имена кортежей (ну, значения ValueTuple имеют поля), в противном случае будут использоваться имена по умолчанию, как вы видели:

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

Теперь кортежи имеют красиво названные поля, которые вы можете использовать

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

Не забудьте добавить пакет System.ValueTuple из NuGet и имейте в виду, что ValueTuples являются изменяемыми структурами.


Обновление: деконструкция, представленная в настоящее время только как назначение существующим переменным (деконструкция-присваивание) или вновь созданным локальным переменным (деконструкция-декларация). Алгоритм выбора подходящего функционального элемента такой же, как и раньше:

Каждый аргумент в списке аргументов соответствует параметру в объявлении члена функции, как описано в п. 7.5.1.1, и любой параметр, которому не соответствует никакой аргумент, является необязательным параметром.

Переменная Tuple является единственным аргументом. Он не может соответствовать нескольким параметрам в списке формальных параметров метода.

Ответ 3

Деконструкции в С# 7.0 поддерживают три формы:

  • deconstruction-declaration (например, (var x, var y) = e;),
  • деконструкция-назначение (например, (x, y) = e;),
  • и deconstruction-foreach (например, foreach(var(x, y) in e) ...).

Были рассмотрены другие контексты, но, вероятно, они уменьшали полезность, и мы не могли завершить их в таймфрейме С# 7.0. Деконструкция в позиции let (let (x, y) = e ...) и в лямбдах представляется хорошим кандидатом для будущего расширения.

Последнее обсуждается в https://github.com/dotnet/csharplang/issues/258

Прошу прокомментировать ваши отзывы и интерес, так как это поможет продвигать предложения.

Подробнее о том, что было включено в деконструкцию С# 7.0 в doc.

Ответ 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);

Он будет работать.