Eric Lippert ha scritto un'eccellente serie di articoli sui limiti (e sulle decisioni di progettazione che influenzano tali scelte) sui blocchi iteratori
In particolare, i blocchi iteratori sono implementati da alcune sofisticate trasformazioni del codice del compilatore. Queste trasformazioni avrebbero un impatto con le trasformazioni che avvengono all'interno di funzioni anonime o lambda in modo tale che in determinate circostanze entrambi proverebbero a "convertire" il codice in qualche altro costrutto incompatibile con l'altro.
Di conseguenza è loro vietato interagire.
Il modo in cui i blocchi iteratori funzionano sotto il cofano è trattato bene qui .
Come semplice esempio di 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;
}
return items.ToList();
}
Il compilatore desidera contemporaneamente convertirlo in qualcosa di simile:
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 allo stesso tempo l'aspetto dell'iteratore sta cercando di fare il suo lavoro per creare una piccola macchina a stati. Alcuni semplici esempi potrebbero funzionare con una discreta quantità di controllo di integrità (prima occupandosi delle chiusure annidate (possibilmente arbitrariamente)) quindi vedendo se le classi risultanti di livello più basso potrebbero essere trasformate in macchine a stati iteratori.
Tuttavia questo sarebbe
- Un bel po 'di lavoro.
- Non potrebbe funzionare in tutti i casi senza almeno che l'aspetto del blocco iteratore sia in grado di impedire all'aspetto di chiusura di applicare determinate trasformazioni per l'efficienza (come la promozione di variabili locali in variabili di istanza piuttosto che una classe di chiusura a tutti gli effetti).
- Se ci fosse anche una minima possibilità di sovrapposizione dove era impossibile o sufficientemente difficile da non implementare, il numero di problemi di supporto risultanti sarebbe probabilmente alto poiché il sottile cambiamento di rottura andrebbe perso per molti utenti.
- Può essere facilmente aggirato.
Nel tuo esempio in questo modo:
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
lambda anonimi che consentono l'await
interno in C # 5.0, sarei interessato a sapere perché non hanno ancora implementato iteratori anonimi conyield
dentro. Più o meno, è lo stesso generatore di macchine a stati.