C# 是委托中的值类型,与父方法中的值类型不同
C# are value types in a delegate different to those in the parent method
考虑以下代码块:
int noThreads = noProcessors - 1;
var countdownEvent = new CountdownEvent(noThreads);
if (noThreads == 0)
noThreads = 1;
float blockSize = (float)jobList.Count / noThreads;
int startIndex, stopIndex;
for (int i = 0; i < noThreads; i++)
{
startIndex = i * (int)blockSize;
stopIndex = (i + 1) * (int)blockSize;
if (i == noThreads - 1)
stopIndex = jobList.Count;
new Thread(delegate()
{
for (int j = startIndex; j < stopIndex; j++)
{
CreateJobAndAddToCollection(jobs[j], jobList, j);
}
countdownEvent.Signal();
}).Start();
}
countdownEvent.Wait();
我想知道委托内的变量 startIndex 和 stopIndex 是否指向与委托外的同名变量相同的位置。从我的测试来看,我认为它们确实如此,这让我感到惊讶,因为它们是值类型,而且我原以为委托中的代码将被视为一个单独的方法。
抱歉,如果之前有人问过这个问题,我找不到任何其他相关信息。
I want to know whether the variables startIndex and stopIndex within the delegate point to the same location as their namesakes outside the delegate.
是的,他们有。变量本身被捕获 - 在委托内所做的更改将在委托外可见,反之亦然。
但是,这只是一个问题,因为您已经在 循环之外声明了 startIndex
和 stopIndex
。如果您在循环中 声明它们,则每次迭代将使用一对单独的变量。
for (int i = 0; i < noThreads; i++)
{
int startIndex = i * (int)blockSize;
int stopIndex = (i + 1) * (int)blockSize;
// Code as before
}
每个委托现在将捕获一个单独的 startIndex
和 stopIndex
对,循环中的其余代码不会修改这些变量,因此您将 "safe".换句话说,如果您的循环代码包含:
开始索引++;
stopIndex++;
最后,委托仍然会看到这些变化(假设新线程在迭代结束后实际执行了委托...)但是 next 上的新变量对 循环的迭代将完全独立于此迭代。
避免线程,这里有一个简单的例子来展示这一切:
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
int outerCounter = 0;
var actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
outerCounter = i * 10;
int innerCounter = i * 10;
// Using an anonymous method here as per question;
// you'd see the same behaviour with a lambda expression.
actions.Add(delegate {
Console.WriteLine("{0}: {1}", outerCounter, innerCounter);
});
innerCounter++;
}
foreach (var action in actions)
{
action();
}
}
}
输出为:
40: 1
40: 11
40: 21
40: 31
40: 41
这表明:
- 所有代表都使用相同的
outerCounter
变量,该变量在 for
循环结束时的值为 40。
- 每个委托都使用自己的
innerCounter
变量,当委托 创建 时,该变量的值为 i * 10
,但 i * 10 + 1
由于 innerCounter++
行,代表 已执行 时。
它们通过引用传递给委托方法。
考虑以下代码块:
int noThreads = noProcessors - 1;
var countdownEvent = new CountdownEvent(noThreads);
if (noThreads == 0)
noThreads = 1;
float blockSize = (float)jobList.Count / noThreads;
int startIndex, stopIndex;
for (int i = 0; i < noThreads; i++)
{
startIndex = i * (int)blockSize;
stopIndex = (i + 1) * (int)blockSize;
if (i == noThreads - 1)
stopIndex = jobList.Count;
new Thread(delegate()
{
for (int j = startIndex; j < stopIndex; j++)
{
CreateJobAndAddToCollection(jobs[j], jobList, j);
}
countdownEvent.Signal();
}).Start();
}
countdownEvent.Wait();
我想知道委托内的变量 startIndex 和 stopIndex 是否指向与委托外的同名变量相同的位置。从我的测试来看,我认为它们确实如此,这让我感到惊讶,因为它们是值类型,而且我原以为委托中的代码将被视为一个单独的方法。 抱歉,如果之前有人问过这个问题,我找不到任何其他相关信息。
I want to know whether the variables startIndex and stopIndex within the delegate point to the same location as their namesakes outside the delegate.
是的,他们有。变量本身被捕获 - 在委托内所做的更改将在委托外可见,反之亦然。
但是,这只是一个问题,因为您已经在 循环之外声明了 startIndex
和 stopIndex
。如果您在循环中 声明它们,则每次迭代将使用一对单独的变量。
for (int i = 0; i < noThreads; i++)
{
int startIndex = i * (int)blockSize;
int stopIndex = (i + 1) * (int)blockSize;
// Code as before
}
每个委托现在将捕获一个单独的 startIndex
和 stopIndex
对,循环中的其余代码不会修改这些变量,因此您将 "safe".换句话说,如果您的循环代码包含:
开始索引++; stopIndex++;
最后,委托仍然会看到这些变化(假设新线程在迭代结束后实际执行了委托...)但是 next 上的新变量对 循环的迭代将完全独立于此迭代。
避免线程,这里有一个简单的例子来展示这一切:
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
int outerCounter = 0;
var actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
outerCounter = i * 10;
int innerCounter = i * 10;
// Using an anonymous method here as per question;
// you'd see the same behaviour with a lambda expression.
actions.Add(delegate {
Console.WriteLine("{0}: {1}", outerCounter, innerCounter);
});
innerCounter++;
}
foreach (var action in actions)
{
action();
}
}
}
输出为:
40: 1
40: 11
40: 21
40: 31
40: 41
这表明:
- 所有代表都使用相同的
outerCounter
变量,该变量在for
循环结束时的值为 40。 - 每个委托都使用自己的
innerCounter
变量,当委托 创建 时,该变量的值为i * 10
,但i * 10 + 1
由于innerCounter++
行,代表 已执行 时。
它们通过引用传递给委托方法。