Eric Lippert hat eine hervorragende Artikelserie über die Einschränkungen (und Entwurfsentscheidungen, die diese Auswahl beeinflussen) für Iteratorblöcke geschrieben
Insbesondere Iteratorblöcke werden durch einige ausgefeilte Compiler-Code-Transformationen implementiert. Diese Transformationen würden sich auf die Transformationen auswirken, die in anonymen Funktionen oder Lambdas stattfinden, so dass beide unter bestimmten Umständen versuchen würden, den Code in ein anderes Konstrukt zu "konvertieren", das mit dem anderen nicht kompatibel ist.
Infolgedessen ist ihnen die Interaktion verboten.
Wie Iteratorblöcke unter der Haube funktionieren, wird hier gut behandelt .
Als einfaches Beispiel für eine Inkompatibilität:
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();
}
Der Compiler möchte dies gleichzeitig in Folgendes konvertieren:
// 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();
}
Gleichzeitig versucht der Iterator-Aspekt, eine kleine Zustandsmaschine zu erstellen. Bestimmte einfache Beispiele funktionieren möglicherweise mit einer angemessenen Überprüfung der Integrität (zuerst mit den (möglicherweise willkürlich) verschachtelten Abschlüssen) und dann, ob die resultierenden Klassen der untersten Ebene in Iterator-Zustandsmaschinen umgewandelt werden können.
Dies wäre jedoch
- Ziemlich viel Arbeit.
- Könnte möglicherweise nicht in allen Fällen funktionieren, ohne dass zumindest der Iteratorblockaspekt verhindern könnte, dass der Abschlussaspekt bestimmte Transformationen aus Effizienzgründen anwendet (z. B. das Heraufstufen lokaler Variablen zu Instanzvariablen anstelle einer vollwertigen Abschlussklasse).
- Wenn es sogar eine geringe Wahrscheinlichkeit einer Überlappung gäbe, bei der es unmöglich oder ausreichend schwierig wäre, nicht implementiert zu werden, wäre die Anzahl der daraus resultierenden Supportprobleme wahrscheinlich hoch, da die subtile Änderung bei vielen Benutzern verloren gehen würde.
- Es kann sehr leicht umgangen werden.
In Ihrem Beispiel so:
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 zulassen könnenawait
, würde mich interessieren, warum sie noch keine anonymen Iteratoren mityield
Inside implementiert haben . Mehr oder weniger ist es der gleiche Generator für Zustandsmaschinen.