Подтвердить что ты не робот

Напишите метод, который принимает лямбда-выражение

У меня есть метод со следующей сигнатурой:

 void MyMethod(Delegate d){};
 void MyMethod(Expression exp){};
 void MyMethod(object obj){};

Однако это не скомпилируется:

MyMethod((int a) => a)

со следующей ошибкой:

"Cannot convert lambda expression to type 'object' because it is not a delegate type"

Почему это не работает?

Изменить: я знаю, что это работает. В этом случае компилятор компилирует лямбда-выражение в delgate.

void MyMethod(Func<int, int> d){};

С уважением,

4b9b3361

Ответ 1

Поскольку тип System.Delegate не является "делегатом". Это просто базовый класс. Вы должны использовать тип делегата с правильной подписью. Определите свой метод следующим образом:

void MyMethod(Func<int, int> objFunc)

EDIT:

MyMethod (объект) не работает, потому что выражение лямбда не имеет типа в своем собственном, но тип выводится из типа местоположения, которому он назначен. Таким образом, объект тоже не работает. Вы должны использовать тип делегата с правильной подписью.

Ответ 2

void MyMethod(Action<int> lambdaHereLol)
{
    lambdaHereLol(2);
}

в использовании:

var hurrDurr = 5;
MyMethod(x => Console.Write(x * hurrDurr));

С# - статически типизированный язык. Компилятор должен знать Тип всего, с чем он имеет дело. Lambdas немного трудно прибить, и иногда компилятор не может понять это. В моем примере выше, если MyMethod взял объект, компилятор не мог понять, что x является int (мой пример прост, но нет ничего, что говорит, что это не может быть намного сложнее и труднее определить). Поэтому я должен быть более явным в определении метода, который берет мою лямбду.

Ответ 3

Лямбда как (int a) => a будет соответствовать любому делегату, который принимает int и возвращает int. Func<int,int> - всего лишь один пример, и вы можете легко объявить его с помощью delegate int Foo(int x);. Фактически это лямбда-выражение будет даже соответствовать делегату, который принимает int и возвращает a double, потому что результат лямбда (a) неявно конвертируется в double.

Для того, чтобы лямбда была назначена всем типам делегатов, которые она поместила бы, сама лямбда сама по себе не имеет типа. Вместо этого он принимает тип делегата, который вы используете, поскольку, насколько это возможно. ((int a) => a, конечно, нельзя назначить Func<byte, byte>).

Хотя оба делегата Func<int, int> и Foo, которые я определил, могут, конечно, быть преобразованы в Delegate, лямбда не может быть напрямую преобразована в Delegate, потому что неясно, какова будет ее фактическая подпись. После Delegate d = (int a) => a, d будет Foo или Func<int, int>, или даже Func<int, double>? Все это действительные возможности, и компилятор понятия не имеет, что вы намеревались. Это может сделать наилучшее предположение, но С# - это не тот язык, который делает такие догадки. Вот почему вы не можете сделать что-то вроде var = (int a) => a.

Я действительно думаю, что сообщение об ошибке, которое компилятор дает для Delegate d = (int a) => a;, очень неясно:

Невозможно преобразовать лямбда-выражение для типа 'System.Delegate', потому что это не тип делегата

Интуитивно вы думаете, что Delegate является типом делегата, но это не так, как все работает.:)

Ответ 4

Попробуйте следующее:

void MyMethod(Action<int> func) { }

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

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

Ответ 5

Это просто характер компилятора, который вам нужно явно передать объект-делегат Delegate при передаче его как параметра типа Delegate. На самом деле, лямбда-выражения еще более усложняют ситуацию, поскольку в этом случае они не могут быть явно конвертированы в делегатов.

То, что вам требуется, - это двойной бросок, как таковой:

MyMethod((Delegate)(Func<int, int>)((int a) => a));

который, конечно, соответствует сигнатуре метода:

void MyMethod(Delegate d);

В зависимости от вашей ситуации вы можете определить параметр типа Func<int>, а не Delegate (хотя я бы смутился, добавляя перегрузку, просто потому, что он добавляет излишнюю сложность в честности).

Ответ 6

Причиной этого является то же самое, что и выражение типа "object del = (int a) = > a" или даже "var del = (int a) = > a" не работает. Вы можете подумать, что компилятор может определить тип вашего лямбда-выражения, поскольку вы явно указываете тип аргумента, но даже зная, что выражение принимает int и возвращает int, существует несколько типов делегатов, которые он мог бы преобразовать к. Тип делегирования Func является наиболее используемым для таких общих функций, как это, но это всего лишь соглашение и ничего не известно компилятору.

Что вам нужно сделать, так это приведение выражения лямбда к конкретному типу делегата, чтобы компилятор выбрал перегрузку делегата, используя либо обычный синтаксис (Func) ((int a) = > a), либо используя синтаксис конструктора делегата new Func ((int a) = > a).

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