Eric Lippert a écrit une excellente série d'articles sur les limitations (et les décisions de conception influençant ces choix) sur les blocs d'itérateurs
En particulier, les blocs d'itérateur sont implémentés par des transformations de code de compilateur sophistiquées. Ces transformations auraient un impact sur les transformations qui se produisent à l'intérieur des fonctions anonymes ou lambdas de telle sorte que dans certaines circonstances, ils essaieraient tous les deux de «convertir» le code en une autre construction incompatible avec l'autre.
En conséquence, ils sont interdits d'interaction.
Le fonctionnement des blocs d'itérateur sous le capot est bien traité ici .
A titre d'exemple simple d'incompatibilité:
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();
}
Le compilateur souhaite simultanément convertir ceci en quelque chose comme:
// 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();
}
et en même temps, l'aspect itérateur essaie de faire son travail pour créer une petite machine à états. Certains exemples simples peuvent fonctionner avec une bonne quantité de vérification de la cohérence (en traitant d'abord les fermetures imbriquées (éventuellement arbitrairement)) puis en vérifiant si les classes résultantes de niveau inférieur pourraient être transformées en machines à états d'itération.
Cependant ce serait
- Beaucoup de travail.
- Cela ne pourrait pas fonctionner dans tous les cas sans au moins l'aspect bloc d'itérateur pouvant empêcher l'aspect fermeture d'appliquer certaines transformations pour plus d'efficacité (comme la promotion de variables locales en variables d'instance plutôt qu'une classe de fermeture à part entière).
- S'il y avait même un léger risque de chevauchement là où il était impossible ou suffisamment difficile de ne pas être mis en œuvre, le nombre de problèmes de support qui en résulterait serait probablement élevé car le changement subtil serait perdu pour de nombreux utilisateurs.
- Cela peut être très facilement contourné.
Dans votre exemple comme ceci:
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 anonymes autorisant l'await
intérieur en C # 5.0, je serais intéressé de savoir pourquoi ils n'ont toujours pas implémenté d'itérateurs anonymes avecyield
inside. Plus ou moins, c'est le même générateur de machine d'état.