C# Odds, Part III: Beware of anonymous delegates

Consider the following code:

public class TestEvent {
  public event EventHandler FireUp;
  public void InvokeFireUp() {
    if (FireUp != null) FireUp(this, EventArgs.Empty);
  }
}

class Program {
  static void Main(string[] args) {
    List<TestEvent> fires = new List<TestEvent>();
    for (int i = 0; i < 10; i++) {
      TestEvent t = new TestEvent();
      t.FireUp += delegate { Console.WriteLine(i); };
      fires.Add(t);
    }

    foreach (TestEvent t in fires) t.InvokeFireUp();
    Console.ReadLine();
  }
}

If you are expecting it to print the numbers from 1 to 10 then you’re wrong :-) The thing is, external variables used in anonymous delegates are passed by reference (even value types like int). In the loop, the variable i is not recreated but increased by one. In the end, i will contain the last value and every event fired will simply write down the value 10. To work as intended, you should write the loop as follows:

for (int i = 0; i < 10; i++) {
  int j = i;
  TestEvent t = new TestEvent();
  t.FireUp += delegate { Console.WriteLine(j); };
  fires.Add(t);
}

Because j is defined “inside” the loop, it will always be a new variable (and hence a new reference in memory).

Tags: , ,

Leave a Reply