¿Cómo se llama cuando se invoca un método que toma una expresión lambda como parámetro, como Enumerable . Where , sin declarar una variable o parámetro de método en la expresión?
Por ejemplo, estoy familiarizado con esta sintaxis de expresión lambda:
public string GetDigits(string input) { return new String(input.Where(i => Char.IsDigit(i)).ToArray()); }Sin embargo, me sorprendió descubrir que esto también se puede escribir como:
public string GetDigits(string input) { return new String(input.Where(Char.IsDigit).ToArray()); }¿Qué está pasando en ese segundo fragmento, donde el método Char.IsDigit() (aparentemente) se llama con un parámetro implícito? ¿Cómo se llama esta sintaxis?
Los métodos no aceptan lambdas como parámetros. Aceptan delegados como parámetros. Una lambda es solo una forma de crear un delegado.
Otra forma es proporcionar un grupo de métodos, como se hace en su segundo ejemplo, que se puede convertir en un delegado.
Una forma similar es utilizar la función de método anónimo. Sin embargo, esto fue más o menos reemplazado con lambdas cuando se agregaron, por lo que no se ve mucho. Su ejemplo usando esa sintaxis sería:
Func<char, bool> predicate = delegate(char c) { return Char.IsDigit(c); }; Otra forma más sería crear un delegado usando Delegate.CreateDelegate . (Sin embargo, esto no es algo que veas tan a menudo).
Una forma final es tener una variable de delegado que obtuvo de otro lugar. (Que en otro lugar habría creado el delegado usando una de estas otras opciones).
¿Qué está pasando en ese segundo fragmento, donde el método Char.IsDigit() (aparentemente) se llama con un parámetro implícito? ¿Cómo se llama esta sintaxis?
No se está llamando. Ese es todo el punto. Estamos tratando de crear un delegado. Un delegado es un objeto que realiza un seguimiento de un método que se invocará y un objeto en el que se debe invocar. Luego puede invocar al delegado y llamará al método que se usó para crearlo. Entonces, aquí no está llamando a IsDigit , está creando un delegado que apunta al método IsDigit , y lo llamará cada vez que se invoque ese delegado.
Cuando usa un lambda, está creando un nuevo método, posiblemente en una nueva clase (ninguno de los cuales tiene un nombre al que pueda referirse, pero tendrán uno en tiempo de ejecución) y el cuerpo de ese método anónimo llamará a IsDigit . Luego, la lambda se resuelve en un delegado que apunta a ese método anónimo, que mantiene la semántica del otro ejemplo de tener un método que, cuando se llama, llama a un método anónimo que, en su implementación, llama a IsDigit . Está agregando una capa adicional de direccionamiento indirecto (que puede o no optimizarse en tiempo de ejecución) para lograr lo mismo.
La firma de Enumerable.Dónde está:
public static IEnumerable<TSource> Where<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate )Este:
input.Where(i => Char.IsDigit(i))es equivalente a escribir:
Func<char, bool> temp = i => Char.IsDigit(i); input.Where(temp); entonces crea una función anónima con un parámetro i que llama a Char.IsDigit .
Este:
input.Where(Char.IsDigit)es equivalente a
Func<char, bool> temp = Char.IsDigit; input.Where(temp);que es equivalente a:
Func<char, bool> temp = new Func<char, bool>(Char.IsDigit); input.Where(temp); por lo que crea un delegado para Char.IsDigit y luego lo pasa a input.Where .
Entonces, el segundo elimina el "man-in-the-middle" (la función anónima). En este caso particular, es "legal" porque el parámetro i de la función anónima se pasa "tal cual" a Char.IsDigit . Habría sido diferente si fuera:
input.Where(i => !Char.IsDigit(i))en este caso, no puede eliminar el intermediario (la función anónima).
No hay nombre para todo esto (o podría llamar al primero "crear y pasar un delegado a una función anónima" y al segundo "crear y pasar un delegado creado a partir de un grupo de métodos"... pero no son hermosos eslóganes, son más una descripción de lo que estás haciendo)
Debido a que el compilador implícitamente convertirá el grupo de métodos en un delegado si encuentra un solo método que coincida con la firma esperada, en este caso, un delegado que toma un solo char como entrada y devuelve un bool .