我根本无法理解为什么这个简单的代码不起作用。我的预期输出是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));
答案 0 :(得分:7)
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是值类型,所以传递了值的副本而不是引用。这就是为什么你的值没有&#39 ;改变。
对于字符串:除了字符串是不可变的这一事实外,为引用类型分配新引用并不会更改包含相同引用的对象。例如,请考虑:
static void Update(string s)
{
s = "bar";
}
string f = "foo";
Update(f);
Console.WriteLine(f); // foo
为s
分配新引用并不会更改f
,f
stil会保留旧引用,而s
会指向内存中的新位置。这不是因为s
是副本,而不是。如果您更改s
的属性(使用字符串,您可以执行此操作但尝试使用其他引用类型) ,它也会更新f
的属性。它以这种方式工作,因为s
和f
是两个不同的字符串,指向内存中的相同位置。所以{{1} }并不绑定s
。您可以认为它们是这样声明的:
f
唯一的例外是当您将string f = "foo";
string s = f;
s = "bar";
作为f
参数传递时,分配也会更改ref
:
f
答案 1 :(得分:3)
因为它们是值类型,而不是改变列表,您可以使用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!!!
}
我认为更改参数的值是一个糟糕的想法,你不应该这样做,你应该返回一个包含新修改的新字符串。
答案 2 :(得分:2)
原因是因为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