Ерік Ліпперт написав чудову серію статей про обмеження (та дизайнерські рішення, що впливають на цей вибір) для блоків ітераторів
Зокрема, блоки ітераторів реалізовані за допомогою деяких складних перетворень коду компілятора. Ці перетворення впливатимуть на перетворення, які відбуваються всередині анонімних функцій або лямбда, таким чином, що за певних обставин вони обидва намагатимуться "перетворити" код в якусь іншу конструкцію, яка була несумісною з іншою.
В результаті їм заборонено взаємодіяти.
Тут добре розглянуто, як ітераторні блоки працюють під капотом .
Як простий приклад несумісності:
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();
}
Компілятор одночасно хоче перетворити це на щось на зразок:
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();
}
і в той же час аспект ітератора намагається зробити свою роботу, щоб створити маленький автомат. Деякі прості приклади можуть працювати з достатньою кількістю перевірки розумності (спочатку маючи справу з (можливо, довільно) вкладеними закриттями), а потім перевіряючи, чи можуть самі нижчі рівні результуючих класів бути перетворені в ітераторні стан-машини.
Однак це було б
- Досить багато роботи.
- Можливо, не могло працювати у всіх випадках, хоча б принаймні аспект блоку ітераторів не міг перешкодити аспекту закриття застосовувати певні перетворення для ефективності (наприклад, просування локальних змінних до змінних екземпляра, а не повноцінного класу закриття).
- Якби існував хоч невеликий шанс накладання, коли це було неможливо або досить важко не реалізувати, тоді кількість проблем із підтримкою, які виникають, могла б бути великою, оскільки незначні зміни, що порушуються, були б втрачені для багатьох користувачів.
- Це можна дуже легко обійти.
У вашому прикладі приблизно так:
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
лямбди, що дозволяютьawait
всередині в C # 5.0, мені було б цікаво дізнатись, чому вони все ще не реалізували анонімні ітератори зyield
inside. Більш-менш, це той самий генератор державного автомата.