SharePoint Newbie
SharePoint Newbie

Reputation: 6082

Write a method which accepts a lambda expression

I have a method with the following signature:

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

However, this fails to compile:

MyMethod((int a) => a)

with the following error:

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

Why doesn't this work?

Edit: I know that this works. The compiler compiles the lambda expression to a delgate in this case I think.

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

Kind regards,

Upvotes: 26

Views: 22843

Answers (6)

SoftMemes
SoftMemes

Reputation: 5702

The reason this fails is the same reason an expression like "object del = (int a) => a" or even "var del = (int a) => a" fails. You might think that the compiler could figure out the type of your lambda expression since you explicitly give the type of the argument, but even knowing that the expression takes an int and returns an int, there are a number of delegate types it could be converted to. The Func delegate type is the one most used for generic functions such as this, but that is just a convention and nothing the compiler is aware of.

What you need to do is to cast the lambda expression to a concrete delegate type in order to have the compiler select the Delegate overload, either using the normal cast syntax (Func)((int a) => a), or using the delegate constructor syntax new Func((int a) => a).

Also, you normally do not want to use the untyped Delegate class unless you need to invoke something differently depending on the number of argument it accepts. It's almost always better to accept a Func or Action for things like callbacks.

Upvotes: 0

Joren
Joren

Reputation: 14746

A lambda like (int a) => a will fit any delegate that takes an int and returns an int. Func<int,int> is just a single example, and you could easily declare one yourself with delegate int Foo(int x);. In fact this lambda expression will even fit a delegate that takes an int and returns a double, because the result of the lambda (a) is implicitly convertible to double.

In order for a lambda to be assignable to all the delegate types that it would fit, the lambda itself doesn't inherently have a type. Instead it takes on the type of the delegate you're using it as, as long as that's possible. ((int a) => a can't be assigned to Func<byte, byte> of course.)

While both Func<int, int> and the Foo delegate I defined can of course be converted to Delegate, a lambda can not be directly converted to Delegate because it is unclear what its actual signature would then be. After Delegate d = (int a) => a, would d be Foo, or Func<int, int>, or even Func<int, double>? All are valid possibilities, and the compiler has no idea what you intended. It could make a best guess, but C# is not the kind of language that does that kind of guesswork. This is also why you can't do something like var = (int a) => a.

I do think the error message that the compiler gives for Delegate d = (int a) => a; is very unclear:

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

Intuitively you would think Delegate is a delegate type, but that's not how things work. :)

Upvotes: 3

Noldorin
Noldorin

Reputation: 147340

It's simply the nature of the compiler that you need to explicitly cast a delegate object to Delegate when passing it as a parameter of type Delegate. In fact, lambda expressions complicate things even further in that they are not implicitly convertable to delegates in this case.

What you require is a double cast, as such:

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

which of course corresponds to the method signature:

void MyMethod(Delegate d);

Depending on your situation, you may want to define a parameter of type Func<int> rather than Delegate (though I would hesitate adding an overload, just because it adds unnecessary complexity in honesty).

Upvotes: 0

user1228
user1228

Reputation:

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

in use:

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

C# is a statically typed language. The compiler needs to know the Type of everything it deals with. Lambdas are a bit hard to nail down, and sometimes the compiler can't figure it out. In my example above, if MyMethod took an object, the compiler couldn't figure out that x is an int (my example is simple, but there's nothing that says it can't be much more complex and harder to determine). So I have to be more explicit in defining the method that takes my lambda.

Upvotes: 15

Andrew Hare
Andrew Hare

Reputation: 351526

Try this:

void MyMethod(Action<int> func) { }

You need to a strongly-typed delegate as a parameter to the method. The reason the other calls fail is because the C# compiler will not allow you to pass a lambda expression to a method expecting an Object because a lambda expression isn't necessarily always a delegate in all cases. This same rule applies for passing the lambda expression as a Delegate.

When you pass the lambda to a function like I have showed above, the compile can safely assume that you want the lambda expression to be converted to a specific delegate type and does so.

Upvotes: 2

Maximilian Mayerl
Maximilian Mayerl

Reputation: 11357

Because the type System.Delegate isn't a "Delegate". It's just the base class. You have to use a delegate type with the correct signature. Define your Method as follows:

void MyMethod(Func<int, int> objFunc)

EDIT:

MyMethod(object) doesn't work because a lambda expression has no type at it's own, but the type is inferred from the type of the location it is assigned to. So object doesn't work either. You HAVE to use a delegate type with the correct signature.

Upvotes: 21

Related Questions