Eric Lippert escreveu uma excelente série de artigos sobre as limitações (e decisões de design que influenciam essas escolhas) nos blocos iteradores
Em particular, os blocos iteradores são implementados por algumas transformações de código do compilador sofisticadas. Essas transformações impactariam nas transformações que acontecem dentro de funções anônimas ou lambdas, de modo que, em certas circunstâncias, ambos tentariam 'converter' o código em alguma outra construção incompatível com a outra.
Como resultado, eles são proibidos de interação.
O modo como os blocos de iteradores funcionam nos bastidores é bem tratado aqui .
Como um exemplo simples de incompatibilidade:
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();
}
O compilador deseja simultaneamente converter isso para 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();
}
e, ao mesmo tempo, o aspecto do iterador está tentando fazer seu trabalho para fazer uma pequena máquina de estado. Certos exemplos simples podem funcionar com uma boa quantidade de verificação de sanidade (primeiro lidando com os fechamentos aninhados (possivelmente arbitrariamente)) e depois ver se as classes resultantes de nível inferior poderiam ser transformadas em máquinas de estado iterador.
No entanto, isso seria
- Muito trabalho.
- Não poderia funcionar em todos os casos sem, pelo menos, o aspecto do bloco iterador ser capaz de evitar que o aspecto do fechamento aplique certas transformações para eficiência (como promover variáveis locais para variáveis de instância em vez de uma classe de fechamento totalmente desenvolvida).
- Se houvesse uma pequena chance de sobreposição onde fosse impossível ou suficientemente difícil de não ser implementado, o número de problemas de suporte resultantes provavelmente seria alto, uma vez que a alteração sutil seria perdida para muitos usuários.
- Pode ser facilmente contornado.
Em seu exemplo:
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 permitindo oawait
interior no C # 5.0, estou interessado em saber por que eles ainda não implementaram iteradores anônimos com oyield
interior. Mais ou menos, é o mesmo gerador de máquina de estado.