2008-10-03 24 views
273

He leído rápidamente la documentación de Microsoft Lambda Expression.C# Expresiones lambda: ¿Por qué debería usarlas?

Este tipo de ejemplo me ha ayudado a entender mejor, sin embargo:

delegate int del(int i); 
del myDelegate = x => x * x; 
int j = myDelegate(5); //j = 25 

Aún así, no entiendo por qué es tal innovación. Es solo un método que muere cuando la "variable de método" termina, ¿verdad? ¿Por qué debería usar esto en lugar de un método real?

Respuesta

256

Lambda expressions son una sintaxis más sencilla para los delegados anónimos y se pueden usar en cualquier lugar donde se pueda usar un delegado anónimo. Sin embargo, lo opuesto no es verdad; Las expresiones lambda se pueden convertir en árboles de expresiones que permiten mucha magia como LINQ to SQL.

El siguiente es un ejemplo de una expresión LINQ to Objects usando delegados anónimos a continuación las expresiones lambda para mostrar cuánto más fácil en el ojo son:

// anonymous delegate 
var evens = Enumerable 
       .Range(1, 100) 
       .Where(delegate(int x) { return (x % 2) == 0; }) 
       .ToList(); 

// lambda expression 
var evens = Enumerable 
       .Range(1, 100) 
       .Where(x => (x % 2) == 0) 
       .ToList(); 

expresiones lambda y delegados anónimos tienen una ventaja sobre escribir una función separada : implementan closures que pueden permitirle pass local state to the function without adding parameters a la función o crear objetos de uso único.

Expression trees son una nueva característica muy poderosa de C# 3.0 que permite que una API vea la estructura de una expresión en lugar de solo obtener una referencia a un método que se puede ejecutar. Una API sólo tiene que hacer un parámetro delegado en un parámetro Expression<T> y el compilador generará un árbol de expresión de una lambda en lugar de un delegado anónimo:

void Example(Predicate<int> aDelegate); 

denominado como:

Example(x => x > 5); 

se convierte en:

void Example(Expression<Predicate<int>> expressionTree); 

Este último se van pasando una representación de la abstract syntax tree que describe la expresión x > 5. LINQ to SQL se basa en este comportamiento para poder convertir expresiones C# en las expresiones SQL deseadas para filtrar/ordenar/etc. en el lado del servidor. limpiado sintaxis delegado anónimo

+0

Sin cierres puede usar métodos estáticos como devoluciones de llamada, pero aún tiene que definir esos métodos en alguna clase, casi aumentando el alcance de dicho método más allá del uso previsto. –

+10

FWIW, usted * puede * tener cierres con un delegado anónimo, por lo que no necesita estrictamente lambdas para eso.Los lambdas son eminentemente más legibles que los delegados anónimos, sin los cuales el uso de Linq te haría sangrar los ojos. – Benjol

4

Muchas veces, solo está utilizando la funcionalidad en un solo lugar, por lo que hacer un método simplemente complica la clase.

11

Se ahorra tener que tener métodos que solo se usan una vez en un lugar específico para que no se definan lejos del lugar donde se usan. Los buenos usos son como comparadores para algoritmos genéricos como la ordenación, donde puede definir una función de clasificación personalizada en la que está invocando el ordenamiento en lugar de alejarse, lo que le obliga a buscar en otra parte para ver qué está ordenando.

Y no es realmente una innovación. LISP ha tenido funciones lambda durante aproximadamente 30 años o más.

124

Las funciones y expresiones anónimas son útiles para los métodos únicos que no se benefician del trabajo adicional requerido para crear un método completo.

Considere este ejemplo:

string person = people.Find(person => person.Contains("Joe")); 

frente

public string FindPerson(string nameContains, List<string> persons) 
{ 
    foreach (string person in persons) 
     if (person.Contains(nameContains)) 
      return person; 
    return null; 
} 

Estos son funcionalmente equivalentes.

+6

¿Cómo se habría definido el método Find() para manejar esta expresión lambda? –

+1

El predicado es lo que espera el método Find. –

+1

Dado que mi expresión lambda coincide con el contrato de Predicado , el método Find() lo acepta. –

3

Es una forma de tomar una operación pequeña y ponerla muy cerca de donde se usa (no muy diferente de declarar una variable cerca de su punto de uso). Se supone que esto hace que tu código sea más legible. Al anonimizar la expresión, también hace que sea mucho más difícil para alguien romper el código de su cliente si la función se usa en otro lugar y se modifica para "mejorarla".

De manera similar, ¿por qué necesita utilizar foreach? Puede hacer todo en foreach con un ciclo simple o simplemente usando IEnumerable directamente. Respuesta: no lo necesita , pero hace que su código sea más legible.

4

La expresión Lambda es una forma concisa de representar un método anónimo. Tanto los métodos anónimos como las expresiones Lambda le permiten definir la implementación del método en línea, sin embargo, un método anónimo requiere explícitamente que defina los tipos de parámetros y el tipo de devolución para un método. La expresión Lambda utiliza la característica de inferencia de tipo de C# 3.0 que permite al compilador inferir el tipo de la variable en función del contexto. ¡Es muy conveniente porque nos ahorra mucho tipeo!

27

Esta es solo una forma de usar una expresión lambda. Puede usar una expresión lambda en cualquier parte puede usar un delegado. Esto le permite hacer cosas como esta:

List<string> strings = new List<string>(); 
strings.Add("Good"); 
strings.Add("Morning") 
strings.Add("Starshine"); 
strings.Add("The"); 
strings.Add("Earth"); 
strings.Add("says"); 
strings.Add("hello"); 

strings.Find(s => s == "hello"); 

Este código será buscar en la lista una entrada que coincide con la palabra "hola".La otra manera de hacer esto es hecho pasar un delegado al método Find, así:

List<string> strings = new List<string>(); 
strings.Add("Good"); 
strings.Add("Morning") 
strings.Add("Starshine"); 
strings.Add("The"); 
strings.Add("Earth"); 
strings.Add("says"); 
strings.Add("hello"); 

private static bool FindHello(String s) 
{ 
    return s == "hello"; 
} 

strings.Find(FindHello); 

EDITAR:

En C# 2.0, esto se podría hacer utilizando la sintaxis delegado anónimo:

strings.Find(delegate(String s) { return s == "hello"; }); 

Lambda ha limpiado significativamente esa sintaxis.

+0

¡Comienzo a descubrir más! Thx –

+2

@Jonathan Holland: Gracias por la edición y agregando la sintaxis del delegado anónimo. Completa el ejemplo muy bien. –

+0

¿Qué es un delegado anónimo? // Lo siento, soy nuevo en C# – HackerMan

33

de Lambda de C# 2.0 ... por ejemplo

Strings.Find(s => s == "hello"); 

se hizo en C# 2.0 así:

Strings.Find(delegate(String s) { return s == "hello"; }); 

Funcionalmente, que hacen exactamente lo mismo, es solo una sintaxis mucho más concisa.

+3

No son * exactamente * lo mismo, como indica @Neil Williams, puedes extraer un AST lambdas utilizando árboles de expresión, mientras que los métodos anónimos no se pueden usar de la misma manera. – ljs

21

Microsoft nos ha dado una manera más limpia y más conveniente de crear delegados anónimos llamados expresiones Lambda. Sin embargo, no se presta mucha atención a la parte de las expresiones de esta declaración. Microsoft lanzó un espacio de nombre completo, System.Linq.Expressions, que contiene clases para crear árboles de expresión basados ​​en expresiones lambda. Los árboles de expresión están formados por objetos que representan la lógica. Por ejemplo, x = y + z es una expresión que podría ser parte de un árbol de expresiones en .Net.Considere el siguiente ejemplo (simple):

using System; 
using System.Linq; 
using System.Linq.Expressions; 


namespace ExpressionTreeThingy 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Expression<Func<int, int>> expr = (x) => x + 1; //this is not a delegate, but an object 
      var del = expr.Compile(); //compiles the object to a CLR delegate, at runtime 
      Console.WriteLine(del(5)); //we are just invoking a delegate at this point 
      Console.ReadKey(); 
     } 
    } 
} 

Este ejemplo es trivial. Y estoy seguro de que estás pensando: "Esto es inútil ya que podría haber creado directamente el delegado en lugar de crear una expresión y compilarla en tiempo de ejecución". Y tú estarías bien. Pero esto proporciona la base para los árboles de expresión. Hay varias expresiones disponibles en los espacios de nombres de Expressions, y puede construir las suyas propias. Creo que puede ver que esto podría ser útil cuando no sabe exactamente qué algoritmo debería ser en diseño o en tiempo de compilación. Vi un ejemplo en alguna parte para usar esto para escribir una calculadora científica. También puede usarlo para los sistemas Bayesian, o para genetic programming (AI). Algunas veces en mi carrera tuve que escribir una funcionalidad tipo Excel que permitía a los usuarios ingresar expresiones simples (sumas, restas, etc.) para operar con los datos disponibles. En pre.Net 3.5 tuve que recurrir a un lenguaje de scripts externo a C#, o tuve que usar la función de emisión de código en la reflexión para crear el código .Net sobre la marcha. Ahora usaría árboles de expresión.

79

Los encontré útiles en una situación en la que quería declarar un controlador para algún evento de control, usando otro control. Para hacerlo normalmente, debe almacenar las referencias de los controles en los campos de la clase para que pueda usarlos en un método diferente al que se crearon.

private ComboBox combo; 
private Label label; 

public CreateControls() 
{ 
    combo = new ComboBox(); 
    label = new Label(); 
    //some initializing code 
    combo.SelectedIndexChanged += new EventHandler(combo_SelectedIndexChanged); 
} 

void combo_SelectedIndexChanged(object sender, EventArgs e) 
{ 
    label.Text = combo.SelectedValue; 
} 

gracias a las expresiones lambda se puede utilizar de esta manera:

public CreateControls() 
{ 
    ComboBox combo = new ComboBox(); 
    Label label = new Label(); 
    //some initializing code 
    combo.SelectedIndexChanged += (s, e) => {label.Text = combo.SelectedValue;}; 
} 

mucho más fácil.

+16

+1 para el ejemplo de un uso real – Nipuna

+0

En el primer ejemplo, ¿por qué no emitir el remitente y obtener el valor? – Andrew

+0

@Andrew: en este sencillo ejemplo, no es necesario usar el emisor, porque solo hay un componente en cuestión y usar el campo directamente guarda un molde, lo que mejora la claridad. En un escenario del mundo real, yo personalmente preferiría usar el remitente también. Por lo general, utilizo un controlador de eventos para varios eventos, si es posible, así que tengo que identificar al remitente real. –

4

Una expresión lambda es como un método anónimo escrito en lugar de una instancia de delegado.

delegate int MyDelagate (int i); 
MyDelagate delSquareFunction = x => x * x; 

en cuenta la expresión lambda x => x * x;

The input parameter value is x (on the left side of =>)

The function logic is x * x (on the right side of =>)

código de una expresión lambda puede ser un bloque de instrucciones en lugar de una expresión.

x => {return x * x;}; 

Ejemplo

Nota: Func es un delegado genérico predefinido.

Console.WriteLine(MyMethod(x => "Hi " + x)); 

    public static string MyMethod(Func<string, string> strategy) 
    { 
     return strategy("Lijo").ToString(); 
    } 

Referencias

  1. How can a delegate & interface be used interchangeably?
5

También puede encontrar el uso de expresiones lambda por escrito códigos genéricos para actuar en sus métodos.

Por ejemplo: función genérica para calcular el tiempo tomado por una llamada a un método. (es decir.Action aquí)

public static long Measure(Action action) 
{ 
    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    action(); 
    sw.Stop(); 
    return sw.ElapsedMilliseconds; 
} 

Y se puede llamar al método anterior utilizando la expresión lambda de la siguiente manera,

var timeTaken = Measure(() => yourMethod(param)); 

Expresión le permite obtener el valor de retorno de su método y fuera parámetro, así

var timeTaken = Measure(() => returnValue = yourMethod(param, out outParam)); 
0

La innovación está en el tipo de seguridad y transparencia. Aunque no declaras los tipos de expresiones lambda, se deducen y se pueden usar mediante búsqueda de código, análisis estático, herramientas de refactorización y reflexión en tiempo de ejecución.

Por ejemplo, antes podría haber usado SQL y podría obtener un ataque de inyección SQL, porque un pirata informático pasó una cadena donde normalmente se esperaba un número. Ahora usaría una expresión LINQ lambda, que está protegida de eso.

No es posible crear una API LINQ en delegados puros, ya que requiere combinar árboles de expresiones juntos antes de evaluarlos.

En 2016, la mayoría de los lenguajes populares tienen soporte lambda expression, y C# fue uno de los pioneros en esta evolución entre los principales lenguajes imperativos.

0

Ésta es quizá la mejor explicación sobre por qué utilizar las expresiones lambda ->https://youtu.be/j9nj5dTo54Q

En resumen, es para mejorar la legibilidad del código, reducir las posibilidades de errores mediante la reutilización en lugar de replicar código y optimización de apalancamiento sucediendo detrás de las escenas .