tutorial expresiones español c# .net lambda anonymous-methods

expresiones - lambda c# tutorial



Cuándo no usar expresiones lambda (9)

Se están respondiendo muchas preguntas en Stack Overflow, con miembros que especifican cómo resolver estos problemas reales de tiempo / mundo utilizando expresiones lambda .

¿Estamos sobreutilizándolo, y estamos considerando el impacto en el rendimiento del uso de expresiones lambda?

Encontré algunos artículos que exploran el impacto en el rendimiento de lambda vs delegados anónimos vs for / foreach loops con diferentes resultados

  1. Delegados anónimos vs Lambda Expressions vs Function Calls Performance
  2. Rendimiento de foreach vs. List.ForEach
  3. .NET / C # Loop Performance Test (FOR, FOREACH, LINQ y Lambda) .
  4. DataTable.Select es más rápido que LINQ

¿Cuáles deberían ser los criterios de evaluación al elegir la solución adecuada? Excepto por la razón obvia de que es un código más conciso y legible cuando se usa lambda.


Aunque me centraré en el punto uno, comienzo dando mis 2 centavos sobre todo el tema del rendimiento. A menos que las diferencias sean grandes o el uso sea intenso, generalmente no me preocupo por los microsegundos que cuando se agregan no representan una diferencia visible para el usuario. Enfatizo que solo no me importa cuando considero métodos llamados no intensivos. Donde sí tengo consideraciones de rendimiento especiales es en la forma en que diseño la aplicación en sí. Me importa el almacenamiento en caché, sobre el uso de hilos, sobre formas inteligentes de llamar a métodos (ya sea para hacer varias llamadas o para intentar hacer una sola llamada), si unir conexiones o no, etc., etc. De hecho, suelo donar no se centran en el rendimiento en bruto, sino en la escalabilidad. No me importa si funciona mejor con una pequeña fracción de nanosegundo para un solo usuario, pero me preocupa mucho poder cargar el sistema con grandes cantidades de usuarios simultáneos sin notar el impacto.

Habiendo dicho eso, aquí va mi opinión sobre el punto 1. Me encantan los métodos anónimos. Me dan una gran flexibilidad y elegancia de código. La otra gran característica de los métodos anónimos es que me permiten usar directamente las variables locales desde el método contenedor (desde una perspectiva C #, no desde una perspectiva IL, por supuesto). Me ahorran mucho código a menudo. ¿Cuándo uso métodos anónimos? Solo una vez el pedazo de código que necesito no es necesario en otro lado. Si se usa en dos lugares diferentes, no me gusta copiar y pegar como técnica de reutilización, así que usaré un simple delegado. Entonces, al igual que respondió shoosh, no es bueno tener duplicación de código. En teoría, no hay diferencias de rendimiento ya que los anónimos son trucos de C #, no cosas de IL.

La mayor parte de lo que pienso sobre los métodos anónimos se aplica a las expresiones lambda, ya que este último se puede usar como una sintaxis compacta para representar métodos anónimos. Supongamos el siguiente método:

public static void DoSomethingMethod(string[] names, Func<string, bool> myExpression) { Console.WriteLine("Lambda used to represent an anonymous method"); foreach (var item in names) { if (myExpression(item)) Console.WriteLine("Found {0}", item); } }

Recibe una serie de cadenas y para cada una de ellas llamará al método que se le haya pasado. Si ese método devuelve verdadero, dirá "Encontrado ...". Puede llamar a este método de la siguiente manera:

string[] names = {"Alice", "Bob", "Charles"}; DoSomethingMethod(names, delegate(string p) { return p == "Alice"; });

Pero, también puedes llamarlo de la siguiente manera:

DoSomethingMethod(names, p => p == "Alice");

No hay diferencia en IL entre los dos, ya que el que usa la expresión Lambda es mucho más legible. Una vez más, no hay impacto en el rendimiento ya que estos son todos trucos del compilador de C # (no trucos del compilador JIT). De la misma manera que no me parece que usemos demasiado los métodos anónimos, no creo que exageremos las expresiones de Lambda para representar métodos anónimos. Por supuesto, la misma lógica se aplica al código repetido: No hagas lambdas, usa delegados regulares. Existen otras restricciones que lo llevan de regreso a métodos anónimos o simples delegados, como pasar o rechazar argumentos.

Las otras cosas buenas de las expresiones Lambda es que la misma sintaxis no necesita representar un método anónimo. Las expresiones Lambda también pueden representar ... adivinaste, expresiones. Toma el siguiente ejemplo:

public static void DoSomethingExpression(string[] names, System.Linq.Expressions.Expression<Func<string, bool>> myExpression) { Console.WriteLine("Lambda used to represent an expression"); BinaryExpression bExpr = myExpression.Body as BinaryExpression; if (bExpr == null) return; Console.WriteLine("It is a binary expression"); Console.WriteLine("The node type is {0}", bExpr.NodeType.ToString()); Console.WriteLine("The left side is {0}", bExpr.Left.NodeType.ToString()); Console.WriteLine("The right side is {0}", bExpr.Right.NodeType.ToString()); if (bExpr.Right.NodeType == ExpressionType.Constant) { ConstantExpression right = (ConstantExpression)bExpr.Right; Console.WriteLine("The value of the right side is {0}", right.Value.ToString()); } }

Observe la firma ligeramente diferente. El segundo parámetro recibe una expresión y no un delegado. La forma de llamar a este método sería:

DoSomethingExpression(names, p => p == "Alice");

Que es exactamente lo mismo que la llamada que hicimos al crear un método anónimo con una lambda. La diferencia aquí es que no estamos creando un método anónimo, sino creando un árbol de expresiones. Es debido a estos árboles de expresiones que podemos traducir expresiones lambda a SQL, que es lo que Linq 2 SQL hace, por ejemplo, en lugar de ejecutar cosas en el motor para cada cláusula, como Where, Select, etc. Lo bueno es que la sintaxis de llamada es la misma ya sea que esté creando un método anónimo o enviando una expresión.


Bueno, cuando hablamos sobre el uso de delegados, no debería haber ninguna diferencia entre los métodos lambda y anónimo: son lo mismo, solo que con una sintaxis diferente. Y los métodos nombrados (utilizados como delegados) también son idénticos desde el punto de vista del tiempo de ejecución. La diferencia, entonces, es entre usar delegados, vs. código en línea - es decir

list.ForEach(s=>s.Foo()); // vs. foreach(var s in list) { s.Foo(); }

(donde esperaría que este último sea más rápido)

E igualmente, si está hablando de cualquier cosa que no sean objetos en memoria, las lambdas son una de sus herramientas más poderosas en términos de mantener la verificación de tipos (en lugar de analizar cadenas todo el tiempo).

Ciertamente, hay casos en que un foreach simple con código será más rápido que la versión de LINQ, ya que habrá menos invocaciones que hacer, e invoca un costo pequeño pero mensurable. Sin embargo, en muchos casos, el código simplemente no es el cuello de botella, y el código más simple (especialmente para agrupar, etc.) vale mucho más que unos pocos nanosegundos.

Tenga en cuenta también que en .NET 4.0 hay nodos de Expression adicionales para cosas como bucles, comas, etc. El lenguaje no los admite, pero sí el tiempo de ejecución. Menciono esto solo por completitud: ¡ciertamente no digo que debas usar la construcción de Expression manual donde lo haría foreach !


Duplicación de código
Si te encuentras escribiendo la misma función anónima más de una vez, no debería ser una.


En cualquier momento, la lambda simplemente pasa sus argumentos directamente a otra función. No cree un lambda para la aplicación de funciones.

Ejemplo:

var coll = new ObservableCollection<int>(); myInts.ForEach(x => coll.Add(x))

Es más agradable como:

var coll = new ObservableCollection<int>(); myInts.ForEach(coll.Add)

La principal excepción es cuando la inferencia de tipo C # falla por cualquier razón (y hay muchas veces que es cierto).


Las expresiones Lambda son geniales. Con respecto a la sintaxis de delegate anterior , tienen algunas ventajas como, pueden convertirse en árboles anónimos de función o expresión, los tipos de parámetros se deducen de la declaración, son más limpios y concisos, etc. No veo valor real para no usar la expresión lambda cuando necesitas una función anónima. Una ventaja no tan grande que tiene el estilo anterior es que puede omitir la declaración de parámetro totalmente si no se utilizan. Me gusta

Action<int> a = delegate { }; //takes one argument, but no argument specified

Esto es útil cuando tiene que declarar un delegado vacío que no hace nada, pero no es una razón suficiente para no usar lambdas.

Lambdas te permite escribir métodos anónimos rápidos. Ahora eso hace que las lambdas no tengan sentido en todos lados donde los métodos anónimos carecen de sentido, es decir, donde los métodos nombrados tienen más sentido. Sobre los métodos nombrados , los métodos anónimos pueden ser desventajosos (no una expresión lambda per se, pero dado que en la actualidad los lambdas representan ampliamente métodos anónimos, es relevante):

  1. porque tiende a conducir a la duplicación lógica (a menudo lo hace, la reutilización es difícil)

  2. cuando es innecesario escribir en uno, como:

    //this is unnecessary Func<string, int> f = x => int.Parse(x); //this is enough Func<string, int> f = int.Parse;

  3. ya que escribir bloques de iteradores anónimos es imposible.

    Func<IEnumerable<int>> f = () => { yield return 0; }; //impossible

  4. ya que las lambdas recursivas requieren una línea más de rareza, como

    Func<int, int> f = null; f = x => (x <= 1) ? 1 : x * f(x - 1);

  5. bueno, ya que la reflexión es un poco más desordenada, pero eso es discutible ¿no?

Además del punto 3, el resto no son razones fuertes para no usar lambdas.

También vea este thread acerca de lo que es desventajoso acerca de los delegados Func/Action , ya que a menudo se usan junto con expresiones lambda.


Mi respuesta no será popular.

Creo que el 99% de Lambda es siempre la mejor opción por tres razones.

En primer lugar, ABSOLUTAMENTE no hay nada de malo en asumir que sus desarrolladores son inteligentes. Otras respuestas tienen una premisa subyacente de que cada desarrollador, excepto usted, es estúpido. No tan.

En segundo lugar, Lamdas (et al) son una sintaxis moderna, y mañana serán más comunes de lo que son hoy en día. El código de su proyecto debe fluir de las convenciones actuales y emergentes.

En tercer lugar, escribir código "a la antigua usanza" puede parecerle más fácil, pero no es más fácil para el compilador. Esto es importante, los enfoques heredados tienen pocas oportunidades de mejorarse a medida que el compilador es revoked. Lambdas (y otros) que confían en el compilador para expandirlos pueden beneficiarse ya que el compilador los trata mejor con el tiempo.

Para resumir:

  1. Los desarrolladores pueden manejarlo
  2. Todo el mundo lo está haciendo
  3. Hay potencial futuro

De nuevo, sé que esta no será una respuesta popular. Y créanme "Simple is Best" es mi mantra, también. El mantenimiento es un aspecto importante para cualquier fuente. Lo entiendo. Pero creo que estamos eclipsando la realidad con algunas reglas generales cliché.

// Jerry


Reglas de juego:

  1. Escribe tu código para que sea natural y legible.
  2. Evite las duplicaciones de código (las expresiones lambda pueden requerir un poco de diligencia extra).
  3. Optimice solo cuando haya un problema, y ​​solo con datos para respaldar el problema.


Yo diría que las diferencias de rendimiento suelen ser muy pequeñas (y en el caso de los bucles, obviamente, si nos fijamos en los resultados del segundo artículo (por cierto, Jon Skeet tiene un artículo similar here )) que casi nunca debería elegir una solución solo por razones de rendimiento, a menos que esté escribiendo una pieza de software donde el rendimiento sea absolutamente el requisito no funcional número uno y realmente tenga que hacer micro-optimizaciones.

Cuándo elegir qué? Supongo que depende de la situación pero también de la persona. Solo como ejemplo, algunas personas permiten List.Foreach sobre un bucle foreach normal. Yo personalmente prefiero este último, ya que generalmente es más legible, pero ¿quién soy para argumentar en contra de esto?