Reputation: 6082
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
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
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
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
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
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
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