Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anonymous method for event handler in for loop

Can this be done in a for loop?

        TickEventArgs targs1 = new TickEventArgs(lbl1_up_time, _elapsedTime_up1);
        timer_up1.Tick += (sender, e) => Tick(targs1);

        TickEventArgs targs2 = new TickEventArgs(lbl2_up_time, _elapsedTime_up2);
        timer_up2.Tick += (sender, e) => Tick(targs2);

        TickEventArgs targs3 = new TickEventArgs(lbl3_up_time, _elapsedTime_up3);
        timer_up3.Tick += (sender, e) => Tick(targs3);

        TickEventArgs targs4 = new TickEventArgs(lbl4_up_time, _elapsedTime_up4);
        timer_up4.Tick += (sender, e) => Tick(targs4);

        TickEventArgs targs5 = new TickEventArgs(lbl5_up_time, _elapsedTime_up5);
        timer_up5.Tick += (sender, e) => Tick(targs5);

This doesnt work because i is out of bounds (5)

        targs[0] = new TickEventArgs(lbl1_up_time, _elapsedTime_up1);
        targs[1] = new TickEventArgs(lbl2_up_time, _elapsedTime_up2);
        targs[2] = new TickEventArgs(lbl3_up_time, _elapsedTime_up3);
        targs[3] = new TickEventArgs(lbl4_up_time, _elapsedTime_up4);
        targs[4] = new TickEventArgs(lbl5_up_time, _elapsedTime_up5);

        timers[0] = timer_up1;
        timers[1] = timer_up2;
        timers[2] = timer_up3;
        timers[3] = timer_up4;
        timers[4] = timer_up5;

        int i = 0;

        for (i = 0; i <= 4; i++)
        {
            timers[i].Tick += (sender, e) => Tick(targs[i]);
        } 
like image 598
dirtyw0lf Avatar asked Aug 24 '13 23:08

dirtyw0lf


People also ask

What are anonymous methods and objects example?

Anonymous methods provide a technique to pass a code block as a delegate parameter. Anonymous methods are the methods without a name, just the body. You need not specify the return type in an anonymous method; it is inferred from the return statement inside the method body.

What are anonymous methods?

Anonymous method is a block of code, which is used as a parameter for the delegate. An anonymous method can be used anywhere. A delegate is used and is defined in line, without a method name with the optional parameters and a method body. The scope of the parameters of an anonymous method is the anonymous-method-block.

What are the limitations of the anonymous method?

Anonymous Method LimitationsIt cannot contain jump statement like goto, break or continue. It cannot access ref or out parameter of an outer method. It cannot have or access unsafe code. It cannot be used on the left side of the is operator.

What is an anonymous method and how is it different from a lambda expression?

Anonymous Method is an inline code that can be used wherever a delegate type is expected. Microsoft introduced Anonymous Methods in C# 2.0 somewhere around 2003. Lambda expression is an anonymous method that you can use to create delegates or expression tree types.


2 Answers

This is coming from the lambda expression; i is shared between all of them. By the time the function is executed they're essentially being called like timers[i].Tick += (sender, e) => Tick(targs[5]).

To avoid this, create a locally scoped variable (int locali = i) and use that in your line instead. This will make sure that each lambda expression actually gets the value you expect.

for (i = 0; i <= 4; i++)
{
    int locali = i;
    timers[locali].Tick += (sender, e) => Tick(targs[locali]);
} 

i becomes 5 from the last iteration of your loop before exiting. Naturally, you don't have a targs[5] element, so it throws an IndexOutOfRangeException.

Technically, you don't need to use locali for the timers[i].Tick part since it's evaluated immediately, but I personally find it confusing to mix the two.


Some additional reading on the concepet:

The foreach identifier and closures

Closing over the loop variable considered harmful

like image 66
Chris Sinclair Avatar answered Oct 04 '22 04:10

Chris Sinclair


There is only one i in this case and all of the lambdas are capturing the same value. Use a local that is scoped to the loop so that each lambda has a different copy

for (i = 0; i <= 4; i++)
{
  int j = i;
  timers[j].Tick += (sender, e) => Tick(targs[j]);
}
like image 42
JaredPar Avatar answered Oct 04 '22 03:10

JaredPar