Не удается преобразовать лямбда выражение к типу string так как он не является типом делегата

Обновлено: 05.07.2024

Компилируется в (обратите внимание на публичность полей closure-класса):

Таким образом, переменная, взятая в замыкание, никогда не выделяется на стеке и обладает небольшим оверхедом при доступе, так как является полем closure-класса. Существуют вырожденные случаи, когда в замыкание попадают только поля класса:

В этих случаях делегат очень удобно компилируется в метод уровня экземпляра:

За счёт этого же эффекта, несколько вложенных определений анонимных методов:

Могут эффективно компилироваться всего лишь в один closure-класс:

Но стоит взять в замыкание ещё одну переменную, как трансформация лямбда-выражения усложняется:

Что вызывает генерацию closure-класса:

Неприятные эффекты начинаются тогда, когда несколько анонимных делегатов в одном методе захватывают одну и ту же переменную:

Видите проблему? Не смотря на то, что один делегат тут даже вовсе не используется, второй делегат продляет жизнь не только переменной value , на которую он замыкается, но ещё и хранит в себе переменную xs ! Таким образом могут появляться трудноотлавливаемые утечки памяти, ведь пользователь никак не ожидает, что делегат сохраняет в себе ссылку на переменную, которую он вовсе не брал в замыкание. Правильной трансформацией было бы использование двух closure-классов: первый хранил бы в себе переменную index , а второй - ссылку на первый closure-класс и переменную xs .

Проблема здесь в том, что вы определили анонимный метод, который возвращает, string но пытается назначить его напрямую string . Это выражение, которое при вызове производит, а string не напрямую string . Его необходимо назначить совместимому типу делегата. В этом случае самый простой выбор - Func

Это можно сделать в одной строке с помощью небольшого приведения типов или использования конструктора делегата, чтобы установить тип лямбда-выражения, за которым следует вызов.

Примечание. Оба образца можно сократить до формы выражения, в которой отсутствует

@ 4thSpace это можно сделать в одну строку с каким-то злым кастингом. Я обновил свой ответ, чтобы показать дорогу

Отлично! Если бы я хотел передать int, можете ли вы показать это в одной строке? Я пробовал это, но не пошел: ((Func ) ((4) => )) ();

Вы пытаетесь назначить делегата функции строковому типу. Попробуй это:

Теперь вы можете выполнить функцию следующим образом:

Используя небольшую вспомогательную функцию и дженерики, вы можете позволить компилятору определить тип и немного сократить его:

Боковое примечание: это также хорошо, так как тогда вы можете вернуть анонимный тип:

Интересная техника. Добавляет ли это накладные расходы времени выполнения или все это происходит во время компиляции?

@ToolmakerSteve: Я предполагаю, что это добавит незначительные накладные расходы времени выполнения (он переносит вызов анонимного метода в другой метод) - однако я подозреваю, что это также будет зависеть от того, где был определен метод FuncInvoke (та же сборка, что и где он вызывается по сравнению с другой сборкой и т. д.), поскольку это может быть что-то вроде того, что компилятор может "встроить". Это тот вопрос, на который люди отвечают, написав быструю тестовую программу, компилируя и затем разбирая получившийся IL.

Лямбда-выражение можно разделить на параметры с последующим исполнимым кодом, например:

Левая часть представляет собой ноль или более параметров с последующим символом лямбда => который применяется для отделения объявления параметра от реализации метода. Далее за лямбда-выражением идет тело оператора.

Лямбда-выражение позволяет передавать функции в качестве аргументов вызову метода. Начнем с простого примера лямбда-выражения, возвращающего четные числа из списка целых чисел.

Взглянув на первое лямбда-выражение, присвоенное переменной evens, вы заметите несколько отличий от безымянных методов. Первое – в коде нигде не используется ключевое слово delegate. Второе – не определены типы параметров и возвращаемой переменной, потому что компилятор логически выводит тип на основе контекста. Типы в выражении определяются определением delegate. В данном случае тип возвращаемой переменной, заданный методом FindAll, принимает делегат, принимающий параметр int и возвращающий логическое значение. Лямбда-выражение без фигурных скобок и типа возвращаемой переменной является самым кратким способом представления безымянного метода. Если число параметров равно единице, то можно опустить круглые скобки, окружающие параметр, как показано в первом лямбда-выражении. Хотя лямбда-выражение не требует явных параметров, можно определить параметры, фигурные скобки и тип возвращаемой переменной, как показано во втором лямбда-выражении, присвоенном переменной even2.

Используется явный параметр int и тип возвращаемой переменной, обычно задаваемые в методе. Оператор возврата не будет работать, если не закрыть исполняемый код круглыми скобками, учитывая, что вы полностью оговорили все, что относится к методу.

Другое место, где требуются круглые скобки в лямбда-выражении, - когда вы хотите использовать параметр в нескольких блоках кода внутри лямбда-выражения следующим образом:


В примере кода выше код был заключен в фигурные скобки, чтобы можно было использовать параметр в обоих выражениях. Без фигурных скобок компилятор не смог бы распознать переменную i.

Можно использовать лямбда-выражения, если делегат не имеет параметров. В таком случае надо задать пару пустых круглых скобок, чтобы обозначить метод без параметров. Ниже дан простой пример, показывающий лямбда без параметров.


Если ваш метод или делегат не отвечает условиям, то вам придется вручную объявить делегат, принимающий эти параметры. Обобщенные делегаты обычно охватывают большинство сценариев, но в случаях, когда он не удовлетворяет ваши нужды, пишите пользовательский делегат.

Бывают случаи, когда выведение типа не возвращает тип данных, который вам нужно, чтобы возвращало лямбда-выражение. В таких случаях можно явно задать тип параметра в лямбда-выражении. Например:

Выражение выше возвращает ошибку компилятора, так как при делении double выведенный тип на самом деле будет double. Однако вы назначаете лямбда-выражение делегату, имеющему целый тип возвращаемой переменной. Если на самом деле вы хотите возвращать int из метода, преобразуйте тело выражения в int, чтобы указать ваше намерение, как показано ниже:

Лямбда-выражения бывают двух типов. Первый является простым выражением, в котором все выводится, и состоит только из выражения. Второй тип лямбда-выражения является блоками операторов, состоящими из фигурных скобок и возвращаемого типа. Напишем лямбда-выражение в обеих формах, чтобы увидеть разницу:

Пойдем дальше и разберем каждое лямбда-выражение поочередно. Первое лямбда-выражение является простым выражением, не имеющим тела оператора, так как нет оператора возврата и фигурных скобок, тогда как второе лямбда-выражение содержит тело оператора, так как имеет оператор возврата и фигурные скобки. Хотя оба выражения компилируются в делегат, преимущество лямбда-выражений без тела оператора заключается в том, что они могут быть преобразованы в дерево выражений, которое конкретный поставщик может использовать для генерации своей собственной реализации. Подобно LINQ для SQL, это преобразовывает дерево выражений в его язык предметной области, именуемый SQL, и отправляет его в базу данных. Третье лямбда-выражение показывает отличие лямбда-выражения от безымянного метода. Красота данного оператора заключается в том, что его легко превратить в выражение, тогда как безымянный метод можно превратить только в делегат. Прекрасно, что выражение можно превратить обратно в делегат путем компиляции выражения в делегат с помощью следующего синтаксиса:

Последнее лямбда-выражение генерирует исключение, потому что компилятор не может преобразовать лямбда-выражение, содержащее тело оператора, о чем говорит то, что оно окружено фигурными скобками и оператором возврата.

Хотя можно использовать лямбда-выражения для генерации деревьев выражений, ничто не мешает вам прямо создать свое собственное дерево выражений. Разберем пример создания дерева выражения для лямбда-выражения square = x => x * x.

Начнем с выражения параметра типа int.

Следующий шаг - создать тело лямбда-выражения, являющееся двоичным выражением. Тело состоит из оператора умножения для одного и того же выражения параметра.

Заключительный шаг - создать лямбда-выражение, соединяющее тело с параметром следующим образом:

Последний шаг превращает выражение в делегат и выполняет делегат следующим образом:

Вы определили свой метод как метод расширения, поэтому для целей вашего примера вы можете игнорировать первый параметр в объявлении метода.

Следовательно, единственный параметр, который вас интересует, - это второй int pKProgramID ,.

Когда вы вызываете этот метод (рекурсивно), он ожидает int, но вы передаете лямбда-делегат ( p => p.PKProgramID == pKProgramID )

И, как намекнул Раймонд Чен в своем комментарии, ваш метод рекурсивен, так что впереди вас может быть еще больше боли!

James Wiseman

Встроенного удаления на основе предикатов нет. Вам нужно будет использовать:

или поскольку мы предполагаем, что ПК уникален:

Конечно, SubmitChanges() в какой-то момент вам также понадобится контекст.

Читайте также: