Я подумал, что было бы неплохо сделать что-то вроде этого (с лямбдой, возвращающей yield):
public IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new()
{
IList<T> list = GetList<T>();
var fun = expression.Compile();
var items = () => {
foreach (var item in list)
if (fun.Invoke(item))
yield return item; // This is not allowed by C#
}
return items.ToList();
}
Однако я обнаружил, что не могу использовать yield в анонимном методе. Интересно почему. Документы по выходу просто говорят, что это запрещено.
Поскольку это было запрещено, я просто создал List и добавил в него элементы.
c#
yield
anonymous-methods
yield-return
Лэнс Фишер
источник
источник
async
лямбды, разрешающиеawait
внутри в C # 5.0, мне было бы интересно узнать, почему они до сих пор не реализовали анонимные итераторы сyield
внутри. Более-менее, это тот же генератор конечных автоматов.Ответы:
Эрик Липперт недавно написал в блоге серию сообщений о том, почему в некоторых случаях доходность недопустима.
РЕДАКТИРОВАТЬ2:
Вы, наверное, найдете там ответ ...
РЕДАКТИРОВАТЬ1: это объясняется в комментариях к Части 5, в ответе Эрика на комментарий Абхиджита Пателя:
Вопрос:
А:
источник
Эрик Липперт написал отличную серию статей об ограничениях (и проектных решениях, влияющих на этот выбор) блоков итераторов.
В частности, блоки итератора реализуются посредством некоторых сложных преобразований кода компилятора. Эти преобразования будут влиять на преобразования, которые происходят внутри анонимных функций или лямбда-выражений, так что в определенных обстоятельствах они оба будут пытаться «преобразовать» код в какую-то другую конструкцию, несовместимую с другой.
В результате им запрещено взаимодействие.
Здесь хорошо разбирается, как работают блоки итератора .
В качестве простого примера несовместимости:
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(); }
Компилятор одновременно хочет преобразовать это во что-то вроде:
// 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(); }
и в то же время аспект итератора пытается сделать свою работу по созданию небольшого конечного автомата. Некоторые простые примеры могут работать с достаточным количеством проверок работоспособности (сначала имея дело с (возможно, произвольными) вложенными замыканиями), а затем проверять, могут ли результирующие классы самого нижнего уровня быть преобразованы в конечные машины итератора.
Однако это было бы
В вашем примере так:
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; }
источник
Magic
класс должен бытьMagic<T>
.К сожалению, я не знаю, почему они не разрешили этого, поскольку, конечно, вполне возможно представить себе, как это будет работать.
Однако анонимные методы уже являются частью «магии компилятора» в том смысле, что метод будет извлечен либо в метод в существующем классе, либо даже в целый новый класс, в зависимости от того, имеет ли он дело с локальными переменными или нет.
Кроме того, использование методов итератора
yield
также реализовано с помощью магии компилятора.Я предполагаю, что один из этих двух делает код неидентифицируемым для другого волшебства, и что было решено не тратить время на выполнение этой работы для текущих версий компилятора C #. Конечно, это может быть вовсе не осознанный выбор и что он просто не работает, потому что никто не подумал его реализовать.
Для 100% точного вопроса я бы посоветовал вам использовать сайт Microsoft Connect и сообщить о вопросе, я уверен, вы получите что-то полезное взамен.
источник
Я бы сделал это:
IList<T> list = GetList<T>(); var fun = expression.Compile(); return list.Where(item => fun.Invoke(item)).ToList();
Конечно, для метода Linq вам понадобится System.Core.dll, на который есть ссылка в .NET 3.5. И включают:
using System.Linq;
Привет,
Хитрый
источник
Возможно, это просто синтаксическое ограничение. В Visual Basic .NET, который очень похож на C #, вполне возможно, хотя и неудобно писать
Sub Main() Console.Write("x: ") Dim x = CInt(Console.ReadLine()) For Each elem In Iterator Function() Dim i = x Do Yield i i += 1 x -= 1 Loop Until i = x + 20 End Function() Console.WriteLine($"{elem} to {x}") Next Console.ReadKey() End Sub
Также обратите внимание на круглые скобки
' here
; лямбда-функцияIterator Function
...End Function
возвращает ,IEnumerable(Of Integer)
но сама не является таким объектом. Его необходимо вызвать, чтобы получить этот объект.Преобразованный код [1] вызывает ошибки в C # 7.3 (CS0149):
static void Main() { Console.Write("x: "); var x = System.Convert.ToInt32(Console.ReadLine()); // ERROR: CS0149 - Method name expected foreach (var elem in () => { var i = x; do { yield return i; i += 1; x -= 1; } while (!i == x + 20); }()) Console.WriteLine($"{elem} to {x}"); Console.ReadKey(); }
Я категорически не согласен с причиной, приведенной в других ответах, о том, что компилятору сложно справиться. То, что
Iterator Function()
вы видите в примере VB.NET, специально создано для лямбда-итераторов.В VB есть
Iterator
ключевое слово; у него нет аналога на C #. ИМХО, нет никакой реальной причины, по которой это не функция C #.Итак, если вам действительно очень нужны анонимные функции итератора, в настоящее время используйте Visual Basic или (я не проверял) F #, как указано в комментарии к Части № 7 в ответе @Thomas Levesque (нажмите Ctrl + F для F #).
источник