2010-11-18 7 views
10

En este post la solución al problema es:¿Cómo la expresión de linq multi-parámetro inicializa su parámetro?

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

El concepto de múltiples parámetros (es decir (item, index)) es un poco desconcertante para mí y no sé la palabra correcta para reducir mi resultados de google Entonces 1) ¿Cómo se llama eso? Y más importante aún, 2) ¿Cómo se inicializan las variables no enumerables? En este caso, ¿cómo se compila index como int y se inicializa a 0?

Gracias.

+0

tenga en cuenta que tenía 'item' y' index' invertidos; parece que ha sido corregido por una edición. –

Respuesta

12

expresiones lambda tienen varias opciones de sintaxis:

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

La sutileza aquí es que Where tiene una sobrecarga que acepta una Func<T, int, bool>, que representa el valor y índice respectivamente (y devolver el bool para el partido) Por lo tanto, es la implementación Where que suministra el índice - algo así como:

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

¿Hay alguna manera de inicializar el índice a algo que no sea 0? –

+0

@Edward en la implementación central? no.Pero podría agregar un número fijo a 'index' en la expresión, es decir' list [index + offset] '. En una implementación personalizada, puedes hacer lo que quieras, pero no estoy seguro de la sabiduría/necesidad/etc. –

4

Al utilizar LINQ, recuerde que usted está pasando un delegado método para el método Where. La sobrecarga particular de Where que está invocando toma un método con firma Func<T,int,bool>, y llamará a este método para cada artículo en list. Internamente, este método particular está llevando la cuenta para cada elemento iterado, y llamando el delegado suministrado usando este valor como el segundo parámetro:

var result=suppliedDelegate(item,count) 
+0

Esto también se aplica al método 'Seleccionar 'en LINQ – RichK

2

Esta respuesta es un poco más técnico ... Recuerde que lambdas son accesos directos a simplemente sintáctico delegados anónimos (que son métodos anónimos). Editar: También pueden ser árboles de expresión dependiendo de la firma de Where (ver el comentario de Marc).

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

es funcionalmente equivalente a

// 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 El método tiene la firma siguiente:

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

que es equivalente a:

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

Eso es donde el ítem y el índice están definidos.

Detrás de las escenas, Where puede hacer algo como (sólo una suposición, se puede descomponer para ver):

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

modo que es donde se inicializa el índice y se pasa a su delegado (anónimo, lambda, o de otra manera).

+1

" Recuerda que las lambdas son simplemente atajos sintéticos para delegados anónimos " no es verdad; * pueden * ser métodos anónimos, pero también podrían ser árboles de expresión. No hay forma de predecir a menos que conozca la firma 'Dónde', que * normalmente * está determinada por la fuente de datos, es decir, qué' lista' está en el primer ejemplo de código en su publicación. –

+0

@Marc: tienes razón. Ya sea un árbol de expresiones o un método anónimo, las comparaciones siguen siendo idénticas, por lo que el resultado es el mismo. Entonces, ¿los árboles de expresión son típicamente más rápidos ya que todo el árbol puede convertirse en un delegado (menos en la pila, etc.)? Y, por supuesto, si se trata de LINQ to SQL, termina convirtiéndose en SQL, no necesariamente un delegado. –

Cuestiones relacionadas