C# 更新ForEach循环内的变量

C# 更新ForEach循环内的变量,c#,linq,.net-4.0,C#,Linq,.net 4.0,我就是不明白为什么这个简单的代码不起作用。我的预期输出是10和15,但它返回2和3。这意味着更新不起作用 列表编号=新列表; 编号2.Add2; 数字3; numbers.ForEachn=>n=n*5; numbers.ForEachn=>Console.WriteLinen; 注意:我已经搜索了很多,但我无法理解这种行为 我该怎么修 更新:字符串的行为相同 List<string> strings = new List<string>(); strings.Add(

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

列表编号=新列表; 编号2.Add2; 数字3; numbers.ForEachn=>n=n*5; numbers.ForEachn=>Console.WriteLinen; 注意:我已经搜索了很多,但我无法理解这种行为

我该怎么修

更新:字符串的行为相同

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;
请记住,List.ForEach循环与ForEach不同,但它只是一个方法,它将操作委托作为参数,并对列表中的每个元素执行指定的操作。因此,它执行类似于源代码中的操作:

 public void ForEach(Action<T> action) 
 {
     // removed unnecessary parts for brevity
     for(int i = 0 ; i < _size; i++) 
     {
         action(_items[i]);
     }
 } 
将新引用指定给s不会更改f,f仍然保留旧引用,s指向内存中的新位置。这不是因为s是副本,而是因为s不是副本。如果使用字符串更改s的属性,则不能更改,但可以尝试使用其他引用类型,它还将更新f的属性。它以这种方式工作,因为s和f是指向内存中相同位置的两个不同字符串。因此s不绑定到f。您可以认为它们是这样声明的:

string f = "foo";
string s = f;
s  = "bar";
唯一的例外是,当您将f作为ref参数传递时,赋值也会更改f:


原因是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; }
        }
    }

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

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

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

Main()
{  
var hello = "hello";
Fn(hello);
Console.WriteLine (hello); // prints hello again!!
}
但是,如果要更改引用,则必须使用ref关键字


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

这是因为整数是值类型,是按值传递的,而不是按引用传递的。我使用字符串得到相同的结果。我不能修改ForEach循环中的字符串。@Ricardo真的吗?您正在执行什么操作?@Ricardo.NET字符串是不可变的-一旦创建它们就不能更改。任何时候修改字符串,实际上都是在创建一个新的、修改过的字符串副本。@Ricardo字符串是不可变的,但是如果您传递了其他引用类型,它会按预期工作,您可以在下面看到我的答案。注意,如果您分配给变量而不是改变值,那么引用类型也是一样的。很好的解释!Ty。我很确定OP不只是希望有人修复他们的代码,而是想解释发生了什么。Ty@Sleiman,这是迄今为止最好的代码修复方法,但我还是想更好地理解为什么ForEach不适用于字符串,因为它们使用引用而不是值。@Ricardo,即使将引用类型传递给函数,它传递引用的副本,而不是实际引用
static void Update(ref string s)
{
     s = "bar";
}

string f = "foo";
Update(ref f);
Console.WriteLine(f); // bar
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
var newList= numbers.Select(n => n = n*5);
void Fn(string s)
{
  s = "not being changed";
}   

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

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