Kyle
Kyle

Reputation: 4298

Lambda with BackgroundWorker

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

Answers (4)

Haney
Haney

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

Dmitry
Dmitry

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

Tim S.
Tim S.

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

Bill
Bill

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

Related Questions