Action/delegate可以改变它的参数值吗?

249*_*076 5 c# linq foreach extension-methods anonymous-function

在测试一个简单的ForEach扩展方法时,我遇到了一个意想不到的结果.

ForEach 方法

public static void ForEach<T>(this IEnumerable<T> list, Action<T> action)
{
    if (action == null) throw new ArgumentNullException("action");

    foreach (T element in list)
    {
        action(element);
    }
}
Run Code Online (Sandbox Code Playgroud)

Test 方法

[TestMethod]
public void BasicForEachTest()
{
    int[] numbers = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    numbers.ForEach(num =>
    {
        num = 0;
    });

    Assert.AreEqual(0, numbers.Sum());
}
Run Code Online (Sandbox Code Playgroud)

为什么numbers.Sum()等于55而不是0?

Chr*_*Wue 5

num是您正在迭代的当前元素的值的副本.所以你只是在改变副本.

你做的基本上是这样的:

foreach(int num in numbers)
{
     num = 0;
}
Run Code Online (Sandbox Code Playgroud)

当然你不希望这改变阵列的内容?

编辑:你想要的是这个:

for (int i in numbers.Length)
{
     numbers[i] = 0;
}
Run Code Online (Sandbox Code Playgroud)

在您的特定情况下,您可以在ForEach扩展方法中维护一个索引,并将其作为第二个参数传递给该操作,然后像这样使用它:

numbers.ForEachWithIndex((num, index) => numbers[index] = 0);
Run Code Online (Sandbox Code Playgroud)

但是一般情况下:创建修改应用它们的集合的Linq样式扩展方法是坏样式(IMO).如果你编写了一个不能应用于你的扩展方法,IEnumerable<T>你应该真正考虑它,如果你真的需要它(特别是当你写的是为了修改集合).你没有太大的收获,但很多东西松散(像意想不到的副作用).我确信有例外,但我坚持这条规则并且它对我很有帮助.