如何避免捕获变量

本文关键字:变量 何避免 | 更新日期: 2023-09-27 17:51:23

我有一个问题

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

当执行DoSomething时,它接收每个捕获变量的最新值,而不是我想要的值。我能想到一个解决办法,但我希望你们能想出更好的办法

如何避免捕获变量

解决这个问题的规范方法是将值复制到临时变量中,这些临时变量在循环中声明为

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);
    }
}

重构为:

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);
}

这很简单,容易理解。它陈述了开发人员的意图,而不会用额外的变量使代码混乱(就像解决这个问题的默认方式一样)。

作为参考,我认为以下内容可以解决我的问题:

foreach(var category in categories)
{
    foreach(var word in words)
    {
        var waitCallback = new WaitCallback(state =>
        {
            var kv = (KeyValuePair<string, string>)state;
            DoSomething(kv.Key, kv.Value);
        });
        var state2 = new KeyValuePair<string, string>(word, category);
        ThreadPool.QueueUserWorkItem(waitCallback, state2);
    }
}

我将把整个代码写成这样,这样就避开了问题,并且对正在发生的事情没有任何疑问:

var callbacks = words.SelectMany(w => categories.Select(c =>
    new WaitCallback(state => {
        DoSomething(w, c);
    })
));
foreach (var callback in callbacks)
    ThreadPool.QueueUserWorkItem(callback);