tags:

views:

961

answers:

6

Hi,

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,

+1  A: 
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.

Will
Anybody else always mix up "cloture" and "closure"?
Will
+1  A: 

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.

Andrew Hare
I know that this works, I want to know why do the other signatures not work.
SharePoint Newbie
+3  A: 

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.

Maximilian Mayerl
I know that this works, I want to know why do the other signatures not work.
SharePoint Newbie
Then read what I have written. System.Delegate is NOT a delegate, a lambda expression can't be converted to System.Delegate. In fact, lambda expressions have no type itself, they get their type from the type of the variable they are assigned to, so object doesn't work either.
Maximilian Mayerl
But I am specifying the type myself. It is not being implicitly inferred, "(int a) => a;". Could you please explain, that lambda expressions have no type itself?
SharePoint Newbie
HAve a look at the C# langauge specification version 3.0, section 6.5 Anonymous function conversions.
Maximilian Mayerl
A: 

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).

Noldorin
This too doesn't work.
SharePoint Newbie
Yeah, because, as I have written, System.Delegate is NOT a delegate. It's just the base type for delegates.
Maximilian Mayerl
Woops, you're right. I missed out the conversion to a delegate type that needs to occur first. This kind of gives an insight into how the C# compiler does implicit conversion of lambda expressions to delegates, and how it then passes it as a parameter. What you see here is doing everything "manually".
Noldorin
A: 

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. :)

Joren
A: 

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.

Freed