Pensé que sería bueno hacer algo como esto (con lambda haciendo un retorno de rendimiento):
public IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new()
{
IList<T> list = GetList<T>();
var fun = expression.Compile();
var items = () => {
foreach (var item in list)
if (fun.Invoke(item))
yield return item; // This is not allowed by C#
}
return items.ToList();
}
Sin embargo, descubrí que no puedo usar el rendimiento en un método anónimo. Me pregunto por qué. Los documentos de rendimiento simplemente dicen que no está permitido.
Como no estaba permitido, simplemente creé la Lista y le agregué los elementos.
c#
yield
anonymous-methods
yield-return
Lance Fisher
fuente
fuente
async
lambdas anónimos que permitan elawait
interior en C # 5.0, me interesaría saber por qué todavía no han implementado iteradores anónimos conyield
inside. Más o menos, es el mismo generador de máquina de estados.Respuestas:
Eric Lippert escribió recientemente una serie de publicaciones de blog sobre por qué no se permite el rendimiento en algunos casos.
EDIT2:
Probablemente encontrará la respuesta allí ...
EDIT1: esto se explica en los comentarios de la Parte 5, en la respuesta de Eric al comentario de Abhijeet Patel:
Q:
UNA :
fuente
Eric Lippert ha escrito una excelente serie de artículos sobre las limitaciones (y las decisiones de diseño que influyen en esas elecciones) de los bloques de iteradores.
En particular, los bloques de iteradores se implementan mediante algunas transformaciones sofisticadas del código del compilador. Estas transformaciones impactarían con las transformaciones que ocurren dentro de funciones anónimas o lambdas de tal manera que en ciertas circunstancias ambos intentarían 'convertir' el código en alguna otra construcción que fuera incompatible con la otra.
Como resultado, se les prohíbe la interacción.
Aquí se trata bien cómo funcionan los bloques de iteradores bajo el capó .
Como un simple ejemplo de incompatibilidad:
public IList<T> GreaterThan<T>(T t) { IList<T> list = GetList<T>(); var items = () => { foreach (var item in list) if (fun.Invoke(item)) yield return item; // This is not allowed by C# } return items.ToList(); }
El compilador quiere convertir esto simultáneamente en algo como:
// inner class private class Magic { private T t; private IList<T> list; private Magic(List<T> list, T t) { this.list = list; this.t = t;} public IEnumerable<T> DoIt() { var items = () => { foreach (var item in list) if (fun.Invoke(item)) yield return item; } } } public IList<T> GreaterThan<T>(T t) { var magic = new Magic(GetList<T>(), t) var items = magic.DoIt(); return items.ToList(); }
y al mismo tiempo, el aspecto del iterador está tratando de hacer su trabajo para hacer una pequeña máquina de estado. Ciertos ejemplos simples podrían funcionar con una buena cantidad de verificación de cordura (primero tratando con los cierres anidados (posiblemente arbitrariamente)) y luego viendo si las clases resultantes del nivel inferior podrían transformarse en máquinas de estado de iterador.
Sin embargo, esto sería
En tu ejemplo así:
public IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new() { return FindInner(expression).ToList(); } private IEnumerable<T> FindInner<T>(Expression<Func<T, bool>> expression) where T : class, new() { IList<T> list = GetList<T>(); var fun = expression.Compile(); foreach (var item in list) if (fun.Invoke(item)) yield return item; }
fuente
Magic
clase debería serloMagic<T>
.Desafortunadamente, no sé por qué no lo permitieron, ya que, por supuesto, es completamente posible imaginar cómo funcionaría.
Sin embargo, los métodos anónimos ya son una pieza de "magia del compilador" en el sentido de que el método se extraerá a un método en la clase existente, o incluso a una clase completamente nueva, dependiendo de si trata con variables locales o no.
Además, los métodos de iterador que se utilizan
yield
también se implementan mediante la magia del compilador.Supongo que uno de estos dos hace que el código no sea identificable para la otra pieza mágica, y que se decidió no dedicar tiempo a hacer que esto funcione para las versiones actuales del compilador de C #. Por supuesto, puede que no sea una elección consciente en absoluto, y que simplemente no funciona porque nadie pensó en implementarlo.
Para una pregunta 100% precisa, le sugiero que use el sitio de Microsoft Connect e informe una pregunta, estoy seguro de que obtendrá algo utilizable a cambio.
fuente
Yo haría esto:
IList<T> list = GetList<T>(); var fun = expression.Compile(); return list.Where(item => fun.Invoke(item)).ToList();
Por supuesto, necesita System.Core.dll referenciado desde .NET 3.5 para el método Linq. E incluye:
using System.Linq;
Salud,
Astuto
fuente
Tal vez sea solo una limitación de sintaxis. En Visual Basic .NET, que es muy similar a C #, es perfectamente posible, aunque incómodo, escribir
Sub Main() Console.Write("x: ") Dim x = CInt(Console.ReadLine()) For Each elem In Iterator Function() Dim i = x Do Yield i i += 1 x -= 1 Loop Until i = x + 20 End Function() Console.WriteLine($"{elem} to {x}") Next Console.ReadKey() End Sub
También tenga en cuenta los paréntesis
' here
; la función lambdaIterator Function
...End Function
devuelve unIEnumerable(Of Integer)
pero no es un objeto en sí mismo. Debe ser llamado para obtener ese objeto.El código convertido por [1] genera errores en C # 7.3 (CS0149):
static void Main() { Console.Write("x: "); var x = System.Convert.ToInt32(Console.ReadLine()); // ERROR: CS0149 - Method name expected foreach (var elem in () => { var i = x; do { yield return i; i += 1; x -= 1; } while (!i == x + 20); }()) Console.WriteLine($"{elem} to {x}"); Console.ReadKey(); }
Estoy totalmente en desacuerdo con la razón dada en las otras respuestas que es difícil de manejar para el compilador. El
Iterator Function()
que ve en el ejemplo de VB.NET se crea específicamente para iteradores lambda.En VB, existe la
Iterator
palabra clave; no tiene contraparte de C #. En mi humilde opinión, no hay una razón real por la que esto no sea una característica de C #.Entonces, si realmente desea funciones de iterador anónimas, actualmente use Visual Basic o (no lo he verificado) F #, como se indica en un comentario de la Parte # 7 en la respuesta de @Thomas Levesque (haga Ctrl + F para F #).
fuente