Eric Lippert napisał doskonałą serię artykułów na temat ograniczeń (i decyzji projektowych wpływających na te wybory) w blokach iteratorów
W szczególności bloki iteratora są implementowane przez pewne wyrafinowane transformacje kodu kompilatora. Przekształcenia te miałyby wpływ na transformacje, które mają miejsce w anonimowych funkcjach lub lambdach, tak że w pewnych okolicznościach obie próbowałyby „przekonwertować” kod na inną konstrukcję, która byłaby niezgodna z drugą.
W rezultacie zabrania się im interakcji.
Sposób działania bloków iteratora pod maską jest tutaj dobrze rozwiązany .
Jako prosty przykład niezgodności:
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();
}
Kompilator jednocześnie chce przekonwertować to na coś takiego:
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();
}
a jednocześnie aspekt iteratora próbuje wykonać swoją pracę, aby stworzyć małą maszynę stanów. Niektóre proste przykłady mogą działać z dużą ilością sprawdzania poprawności (najpierw zajmując się (prawdopodobnie arbitralnie) zagnieżdżonymi zamknięciami), a następnie sprawdzaniem, czy klasy wynikowe z najniższego poziomu można przekształcić w iteratorowe maszyny stanu.
Jednak tak by się stało
- Sporo pracy.
- Nie mógłby działać we wszystkich przypadkach bez przynajmniej aspektu bloku iteratora, który byłby w stanie uniemożliwić aspektowi zamknięcia stosowanie pewnych transformacji w celu zwiększenia wydajności (takich jak promowanie zmiennych lokalnych do zmiennych instancji, a nie w pełni rozwiniętej klasy zamknięcia).
- Gdyby istniała choćby niewielka szansa na nakładanie się, gdy nie było to możliwe lub wystarczająco trudne, aby nie zostać wdrożone, liczba wynikających z tego problemów ze wsparciem byłaby prawdopodobnie wysoka, ponieważ wielu użytkowników utraciłoby subtelną zmianę przełomową.
- Można to bardzo łatwo obejść.
W twoim przykładzie tak:
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
lambdy pozwalająceawait
wewnątrz w C # 5.0, chciałbym wiedzieć, dlaczego nadal nie zaimplementowano anonimowych iteratorów zyield
inside. Mniej więcej jest to ten sam generator maszyny stanów.