Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid captured variables?

I'm having a problem with

foreach(var category in categories)
{
    foreach(var word in words)
    {
        var waitCallback = new WaitCallback(state =>
        {
            DoSomething(word, category);
        });

        ThreadPool.QueueUserWorkItem(waitCallback);
    }
}

When the DoSomething gets executed, it receives the latest value for each captured variable instead of the value I desired. I can imagine a solution for this, but it imagine you guys can come up with better solutions

like image 653
Jader Dias Avatar asked Apr 19 '11 13:04

Jader Dias


2 Answers

The canonical way to solve this is to copy the values into temporary variables which are declared inside the loop.

foreach(var category in categories)
{
    var catCopy = category;
    foreach(var word in words)
    {
        var wordCopy = word;
        var waitCallback = new WaitCallback(state =>
        {
            DoSomething(wordCopy, catCopy);
        });

        ThreadPool.QueueUserWorkItem(waitCallback);
    }
}
like image 195
Heinzi Avatar answered Sep 28 '22 07:09

Heinzi


Refactor it to:

foreach(var category in categories) {
  foreach(var word in words) {
    DoSomethingAsync(word, category);
  }
}

...

private void DoSomethingAsync(string word, string category) {
  var waitCallback = new WaitCallback(state => DoSomething(word, category));
  ThreadPool.QueueUserWorkItem(waitCallback);
}

This is simple and easy to understand. It states the developer's intent without cluttering the code with extra variables (as in the default way to solve this problem).

like image 39
Jordão Avatar answered Sep 28 '22 07:09

Jordão