Как использовать функцию именования ValueTuple с анонимными методами?

Я хотел бы использовать функцию именования ValueTuple следующим образом:

     IEnumerable<(string, char, int)> valueTuples = new(string, char, int)[]
     {
        ("First", '1', 1),
        ("Second", '2', 2),
        ("Third", '3', 3)
     };

     var projection1 = valueTuples.Select(((string s, char c, int i) tuple) => tuple.i);

Но это не компилируется с сообщением об ошибке, которое не очень полезно. Однако они оба компилируются:

     var projection2 = valueTuples.Select(tuple => tuple.Item1);

     var projection3 = valueTuples.Select(((string, char, int) tuple) => tuple.Item1);

При более прямом подходе это НЕ компилируется, НО выдает сообщение об ошибке, которое более полезно:

     var projection4 = Enumerable.Select(
        valueTuples, ((string s, char c, int i) tuple) => tuple.i);

Что приводит к попытке этого, который составляет:

     var projection5 = Enumerable.Select<(string, char, int), int>(
        valueTuples, ((string s, char c, int i) tuple) => tuple.i);

Что в конечном итоге вдохновляет это, что составляет:

     var projection6 = valueTuples.Select<(string, char, int), int>(
        ((string s, char c, int i) tuple) => tuple.i);

Это общая проблема с методами расширения IEnumerable? Это появилось бы не потому, что это компилируется:

     var filtered = valueTuples.Where(((string s, char c, int i) tuple) => tuple.i > 1);

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

1 ответ

Решение

У вас есть список неназванных кортежей (string, char, int) (обратите внимание, что нет никаких имен). По этой причине, доступ с использованием имен по умолчанию Item1, Item2 и так далее отлично работает.

Однако, что вы передаете Select lamdba назван типом кортежа:

(string s, char c, int i) tuple) => tuple.i

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

Но с этим знанием мы можем выяснить, что лучший способ решить вашу проблему - это использовать именованный тип кортежа с самого начала:

IEnumerable<(string s, char c, int i)> valueTuples = new[] {
    ("First", '1', 1),
    ("Second", '2', 2),
    ("Third", '3', 3)
};            
var projection1 = valueTuples.Select(tuple => tuple.i);

Обновление: я действительно нашел проблему по этому поводу в хранилище Roslyn. Это действительно подтвержденная ошибка, и она уже устранена. Там нет никаких объяснений о природе этой ошибки. Исправление должно появиться в версии 15.7.

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

Другие вопросы по тегам