2010-11-18 11 views
10

W tym post rozwiązaniem problemu jest:W jaki sposób wieloparametrowe wyrażenie linq inicjuje ich parametry?

list.Where((item, index) => index < list.Count - 1 && list[index + 1] == item)

Koncepcja Wieloparametrowa (tj (item, index)) jest nieco zaskakujący do mnie, a ja nie wiem poprawne słowo zawężania mój wyniki google. Więc 1) Jak to się nazywa? I co ważniejsze, 2) Jak zainicjować zmienną nierefunkcyjną? W takim przypadku w jaki sposób jest index skompilowany jako int i zainicjowany na 0?

Dzięki.

+0

Zwróć uwagę, że odnaleziono 'item' i' index'; wygląda na to, że zostało to naprawione przez edycję. –

Odpowiedz

12

wyrażenia lambda mieć różne opcje składni:

() => ... // 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 

Subtelność jest to, że Where ma przeciążenie, która akceptuje Func<T, int, bool>, reprezentująca wartość i Indeks odpowiednio (i zwracając bool na mecz). Więc to jest realizacja Where który dostarcza indeks - coś jak:

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; 
     } 
    } 
} 
+1

Czy istnieje sposób na zainicjowanie indeksu do czegoś innego niż 0? –

+0

@Edward w głównej implementacji? Nie.Ale możesz dodać stałą liczbę do 'indeksu' w wyrażeniu, tj.' List [index + offset] '. W niestandardowej implementacji możesz zrobić cokolwiek chcesz, ale nie jestem pewien co do mądrości/potrzeby/itp. –

4

Przy użyciu LINQ, należy pamiętać, że są przechodzącą delegata metody metody Where. Konkretne przeciążenie Where, które wywołujesz, przyjmuje metodę z podpisem Func<T,int,bool> i wywoła tę metodę dla każdego elementu w list. Wewnętrznie ta szczególna metoda jest utrzymanie liczby dla każdego elementu powtórzyć, a nazywając dostarczonego delegata przy użyciu tej wartości jako drugi parametr:

var result=suppliedDelegate(item,count) 
+0

To samo dotyczy metody 'Select' w LINQ – RichK

2

Ta odpowiedź jest trochę bardziej techniczny ... Pamiętaj, że są po prostu lambdas syntatic skróty do anonimowi delegaci (które są metodami anonimowymi). Edycja: Mogą to być drzewa wyrażeń w zależności od podpisu Where (patrz komentarz Marka).

list.Where((item, index) => index < list.Count - 1 && list[index + 1] == item) 

funkcjonalnie równoważne

// 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); 

Metoda Where ma następujący podpis:

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate); 

co odpowiada:

delegate bool WhereDelegate<TSource>(TSource source, int index); 
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, WhereDelegate<TSource> predicate); 

to jest gdzie zdefiniowany jest przedmiot i indeks.

Za kulisami Where może zrobić coś takiego (tylko domyślać, można dekompilować, aby zobaczyć):

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++; 
    } 
} 

więc to, gdzie wskaźnik jest inicjowany i zostanie przeniesiony do swojego delegata (anonimowy, lambda, lub Inaczej).

+1

" Pamiętaj, że lambdy to po prostu syntetyczne skróty do anonimowych delegatów " nie prawda; * mogą * być metodami anonimowymi, ale mogą być również drzewami ekspresji. Nie ma sposobu, aby przewidzieć, o ile nie wiesz podpisu "Gdzie", który jest * zwykle * określany przez źródło danych, tzn. Co 'list' znajduje się w pierwszym przykładzie kodu w twoim poście. –

+0

@Marc: Masz rację. Niezależnie od tego, czy jest to drzewo wyrażeń czy metoda anonimowa, porównania są nadal identyczne, więc wynik jest taki sam. Czy drzewa ekspresji są zazwyczaj szybsze, ponieważ całe drzewo może stać się jednym delegatem (mniej na stosie itp.)? I oczywiście jeśli masz do czynienia z LINQ do SQL, staje się on SQL, niekoniecznie delegatem. –

Powiązane problemy