繁体   English   中英

更新ForEach循环内的变量

[英]Update a variable inside ForEach loop

我根本不明白为什么这个简单的代码不起作用。 我的预期输出是10和15,但返回2和3。这意味着更新不起作用。

List<int> numbers = new List<int>();

numbers.Add(2);
numbers.Add(3);

numbers.ForEach(n => n = n*5);

numbers.ForEach(n => Console.WriteLine(n));


注意:我已经搜索了很多,但是我无法理解这种行为。

我应该如何解决?

更新:字符串的行为相同。

List<string> strings = new List<string>();
strings.Add("a");
strings.Add("b");

strings.ForEach(s => s = s + "--");

strings.ForEach(s => Console.WriteLine(s));

n是列表中当前值的副本,而不是对值的引用。如果要操作列表中的值,请使用for循环

for(int i = 0; i<numbers.Count; i++)
    numbers[i] *= 5;

更详细的解释:

使用普通的foreach循环,您的代码甚至无法编译:

foreach(var n in numbers)
      n = n * 5; // Readonly local variable cannot be used as an assignment target

请记住, List<T>.ForEach循环与foreach ,它只是一个以Action<int>委托作为参数并对列表中的每个元素执行指定操作的方法。 (摘自源代码):

 public void ForEach(Action<T> action) 
 {
     // removed unnecessary parts for brevity
     for(int i = 0 ; i < _size; i++) 
     {
         action(_items[i]);
     }
 } 

如您所见, _item[i]被传递给操作,并且由于int是一个值类型,因此将传递您的值的副本,而不是引用。这就是为什么您的值不变的原因。

对于字符串:除了字符串是不可变的事实之外,为引用类型分配新引用不会更改持有相同引用的对象,例如,请考虑以下事项:

static void Update(string s)
{
     s = "bar";
}

string f = "foo";
Update(f);
Console.WriteLine(f); // foo

s分配新引用不会更改ff stil保留旧引用,并且s指向内存中的新位置。这不是因为s是副本,不是。如果更改s的属性(对于字符串,您不能这样做,但可以尝试使用另一个引用类型),它也会更新f的属性。之所以这样工作是因为sf是两个不同的字符串,它们指向内存中s同一位置s不受f约束。您可以认为它们是这样声明的:

string f = "foo";
string s = f;
s  = "bar";

唯一的例外是,当您将f作为ref参数传递时,赋值也将更改f

static void Update(ref string s)
{
     s = "bar";
}

string f = "foo";
Update(ref f);
Console.WriteLine(f); // bar

因为它们是值类型,所以您可以使用Select创建一个修改后的值,而不是对列表进行变异

var newList= numbers.Select(n => n = n*5);

作为命令式程序员,我们喜欢改变事物,这不是一个绝妙的主意!! 它不适用于字符串的原因是因为默认情况下,C#传递引用的副本而不是实际引用。

void Fn(string s)
{
  s = "not being changed";
}   

Main()
{  
var hello = "hello";
Fn(hello);
Console.WriteLine (hello); // prints hello again!!
}

但是,如果要更改引用,则必须使用ref关键字。

void Fn(ref string s)
{
  s = "Unfortunately, changed!";
}   

Main()
{  
var hello = "hello";
Fn(ref hello);
Console.WriteLine (hello); // Unfortunately, changed!!!
}

我认为更改参数的值是一个糟糕的主意,您不应该这样做,应该返回包含新修改的新字符串。

原因是因为ForEach的参数是通过值而不是通过引用传递的。 但是,如果您确实传递了引用类型,则它必须按预期方式工作,如下所示

class Program
    {
        static void Main(string[] args)
        {
            List<Frog> numbers = new List<Frog>();

            numbers.Add(new Frog { name = "balcha" });
            numbers.Add(new Frog { name = "Tibara" });
            numbers.ForEach(n => n.name = "Bontu");
            numbers.ForEach(n => Console.WriteLine(n.name));
            Console.ReadLine();
        }

        class Frog
        {
            public string name { get; set; }
        }
    }

输出:

 Bontu
 Bontu

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM