Reputation: 4298
Why is the following syntax valid?
BackgroundWorker bw = new BackgroundWorker();
String one = resultFromSomeMethod();
String two = resultFromOtherMethod();
String three = resultFromThirdMethod();
bw.DoWork += (a,b) => TestMethod(one, two, three);
where TestMethod is defined as:
private void TestMethod(String one, String two, String three){
//Do Stuff!!
}
The DoWorkEventHandler
is defined as a delegate that takes two parameters: object sender and EventArgs e. However, the TestMethod above takes no such parameters. By my understanding of a delegate, to create a new delegate the method has to conform to the delegate's declaration. I seem to have bypassed that restriction by using a lambda. How and why does the syntax above work, even though if I try to create a new DoWorkEventHandler(TestMethod)
it most certainly would not work?
I read Eric White's blog on Lambda Expressions but it didn't seem to answer this question.
Upvotes: 3
Views: 1823
Reputation: 34912
Let me clarify lambda's for you:
(a,b) => TestMethod("", "", "");
Translates to:
private void AnonymousLambda(object a, EventArgs b)
{
TestMethod("", "", "");
}
The syntax of a lambda is:
implicit method arguments => method body
The implicit method arguments are determined by the expected signature of what the method is being assigned to (or called from). Because you're assigning this lambda expression to the DoWorkEventHandler
delegate, it automatically interprets a
and b
as object
and EventArgs
respectively.
In some cases, the compiler cannot infer the implicit types for the parameters of the lambda, so you could also have written them explicitly:
(object a, EventArgs b) => TestMethod("", "", "");
So you're really calling your TestMethod in the body of another method which you anonymously created! And the outer method has the DoWorkEventHandler
method signature.
EDIT
Per the additional question details about the string variables, the compiler is creating a closure over your code to include the variables in the scope of the method. The code basically becomes a new class which holds the variables as private fields and then references them in the call:
public class AnonymousClosureClass
{
public void AnonymousLambda(object a, EventArgs b)
{
// Note the reference to the original class instance
String one = originalClassReference.one;
String two = originalClassReference.two;
String three = originalClassReference.three;
TestMethod(one, two, three);
}
}
And your main code effectively becomes:
BackgroundWorker bw = new BackgroundWorker();
// Note: these may become field members to remain visible to the closure class
String one = resultFromSomeMethod();
String two = resultFromOtherMethod();
String three = resultFromThirdMethod();
var closure = new AnonymousClosureClass();
bw.DoWork += closure.AnonymousLambda;
Finally, worth noting, this is not how the actual generated closure code is named or looks, at all.
Upvotes: 10
Reputation: 14059
The delegate's arguments are simply unused. Lambda is an equivalent of the following method:
void LambdaMethod(object a, DoWorkEventArgs b)
{
TestMethod("", "", "");
}
Upvotes: 5
Reputation: 56566
The parameters are defined in the (a,b)
portion of your code. These correspond to object sender
and DoWorkEventArgs e
. That is, your lambda is similar to the following code:
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(MyFunction);
void MyFunction(object a, DoWorkEventArgs b)
{
TestMethod("", "", "");
}
The fact that you're not using the parameters, or returning the value of TestMethod
, is irrelevant.
(compare to a Func<int, int>
myFunc = c => c + 1
lambda equivalent)
int MyFunction(int c)
{
return c + 1;
}
Upvotes: 5
Reputation: 1479
The TestMethod
is being called from the (a,b) function.
Look at it this way instead:
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += DoWorkFunc;
void DoWorkFunc(object a, EventArgs b)
{
TestMethod("", "", "");
}
Upvotes: 5