В этом сообщении решение проблемы:
list.Where((item, index) => index < list.Count - 1 && list[index + 1] == item)
Понятие мультипараметрического (т.е. (item, index)
) немного озадачивает меня, и я не знаю правильного слова, чтобы сузить результаты моего google. Итак, 1) Что называется? И что еще более важно, 2) Как инициализируется неперечислимая переменная? В этом случае, как index
скомпилирован как int и инициализируется 0?
Спасибо.
Ответ 1
Лямбда-выражения имеют различные варианты синтаксиса:
() => ... // no parameters
x => ... // single parameter named x, compiler infers type
(x) => ... // single parameter named x, compiler infers type
(int x) => ... // single parameter named x, explicit type
(x, y) => ... // two parameters, x and y; compiler infers types
(int x, string y) => ... // two parameters, x and y; explicit types
Тонкость здесь заключается в том, что Where
имеет перегрузку, которая принимает значение Func<T, int, bool>
, представляя соответственно значение и индекс (и возвращая bool
для соответствия). Таким образом, реализация Where
предоставляет индекс - что-то вроде:
static class Example
{
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
Func<T, int, bool> predicate)
{
int index = 0;
foreach (var item in source)
{
if (predicate(item, index++)) yield return item;
}
}
}
Ответ 2
При использовании LINQ помните, что вы передаете делегат метода методу Where
. Конкретная перегрузка Where
, которую вы вызываете, принимает метод с сигнатурой Func<T,int,bool>
и будет вызывать этот метод для каждого элемента в list
. Внутренне этот конкретный метод поддерживает подсчет для каждого повторного элемента и вызывает предоставленный делегат, используя это значение в качестве второго параметра:
var result=suppliedDelegate(item,count)
Ответ 3
Этот ответ немного более технический... Помните, что лямбды - это просто синтаксические ярлыки для анонимных делегатов (которые являются анонимными методами).
Изменить: Они также могут быть деревьями выражений в зависимости от сигнатуры Where
(см. комментарий Marc).
list.Where((item, index) => index < list.Count - 1 && list[index + 1] == item)
функционально эквивалентно
// inline, no lambdas
list.Where(delegate(item, index) { return index < list.Count - 1 && list[index + 1] == item; });
// if we assign the lambda (delegate) to a local variable:
var lambdaDelegate = (item, index) => index < list.Count - 1 && list[index + 1] == item;
list.Where(lambdaDelegate);
// without using lambdas as a shortcut:
var anonymousDelegate = delegate(item, index)
{
return index < list.Count - 1 && list[index + 1] == item;
}
list.Where(anonymousDelegate);
// and if we don't use anonymous methods (which is what lambdas represent):
function bool MyDelegate<TSource>(TSource item, int index)
{
return index < list.Count - 1 && list[index + 1] == item;
}
list.Where(MyDelegate);
Метод Where
имеет следующую подпись:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate);
что эквивалентно:
delegate bool WhereDelegate<TSource>(TSource source, int index);
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, WhereDelegate<TSource> predicate);
То, где определены элемент и индекс.
За кулисами Where
может сделать что-то вроде (просто догадаться, вы можете декомпилировать):
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
{
int index = 0;
foreach (TSource item in source)
{
if (predicate(index, source))
yield return item;
index++;
}
}
Итак, когда индекс инициализируется и передается вашему делегату (анонимно, лямбда или иначе).