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;
}
return items.ToList();
}
El compilador quiere convertir esto simultáneamente en algo como:
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
- Mucho trabajo.
- Posiblemente no podría funcionar en todos los casos sin que al menos el aspecto del bloque del iterador pueda evitar que el aspecto del cierre aplique ciertas transformaciones para la eficiencia (como promover variables locales a variables de instancia en lugar de una clase de cierre completa).
- Si hubiera una pequeña posibilidad de superposición donde fuera imposible o lo suficientemente difícil como para no ser implementado, entonces la cantidad de problemas de soporte resultantes probablemente sería alta, ya que muchos usuarios perderían el sutil cambio de ruptura.
- Se puede solucionar muy fácilmente.
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;
}
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.