2009-10-20 7 views
21

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

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){}; 

С наилучшими пожеланиями,

+3

Fyi. Всякий раз, когда что-то не удается скомпилировать, прочитайте (и опубликуйте) сообщение об ошибке. –

+0

@SharePoint Новичок: см. Мой обновленный пост. Это должно решить вашу ошибку сейчас. – Noldorin

ответ

17

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

void MyMethod(Func<int, int> objFunc) 

EDIT:

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

+0

Я знаю, что это работает, я хочу знать, почему другие подписи не работают. –

+0

Тогда прочитайте то, что я написал. System.Delegate НЕ является делегатом, выражение lambda не может быть преобразовано в System.Delegate. На самом деле, лямбда-выражения не имеют самого типа, они получают свой тип от типа переменной, которой они назначены, поэтому объект тоже не работает. –

+0

Но я сам задаю тип. Это не подразумевается неявным образом "(int a) => a;". Не могли бы вы объяснить, что лямбда-выражения не имеют никакого типа? –

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

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

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

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

+1

Кто-нибудь еще всегда смешивает «клоун» и «закрытие»? – Will

+3

+1 для семантики кодера тролля – Filip

+1

«C# - статически типизированный язык. Компилятор должен знать Тип всего, с чем он имеет дело». Это верно, но все остальное не следует. Существует много статически типизированных языков, которые могут вызывать подпись типа для вас. F # - один из примеров. – rgrinberg

2

Попробуйте это:

void MyMethod(Action<int> func) { } 

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

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

+0

Я знаю, что это работает, я хочу знать, почему другие подписи не работают. –

0

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

Что вам требуется двойная литая, как таковой:

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

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

void MyMethod(Delegate d); 

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

+0

Это тоже не работает. –

+0

Да, потому что, как я уже писал, System.Delegate НЕ является делегатом. Это просто базовый тип для делегатов. –

+1

Вордс, ты прав. Я пропустил преобразование в тип делегата, который должен произойти первым. Этот вид дает представление о том, как компилятор C# делает неявное преобразование лямбда-выражений в делегаты и как он передает его в качестве параметра. То, что вы видите здесь, делает все «вручную». – Noldorin

3

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

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

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

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

Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type

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

0

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

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

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

Смежные вопросы