更新ForEach循环内的变量
我简直不明白为什么这个简单的代码不工作。我的预期输出是10和15,但它返回2和3.这意味着更新不起作用。更新ForEach循环内的变量
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
记住循环是不一样的foreach
但它只是需要一个Action<int>
委托作为参数,并执行指定的动作的每一个元素的方法,你list.So它执行像这样(摘自源代码):
public void ForEach(Action<T> action)
{
// removed unnecessary parts for brevity
for(int i = 0 ; i < _size; i++)
{
action(_items[i]);
}
}
正如你可以在这里看到的_item[i]
传递到行动,因为int是你的价值的副本传递值类型,而不是一个reference.And这就是为什么你的价值观没”改变。
对于字符串:除了一个事实,即字符串是不可改变的,分配一个新的参考引用类型并不改变持有相同reference.For例如考虑这个对象:
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
都指向同一位置memory.So 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
的下面看到我的答案请注意,如果你赋值给变量而不是变更值,那么引用类型也是一样的。 – 2014-09-25 23:23:48
@MattiVirkkunen好点我删除了误导性句子 – 2014-09-25 23:26:30
很好的解释! TY。 – Ricardo 2014-09-26 00:44:20
的原因是因为参数传递给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
我相当确信OP不仅仅希望有人修复他们的代码,而是要解释发生了什么事情。 – 2014-09-25 23:24:37
因为他们是值类型,而不是突变的列表,你可以创建一个修改一个使用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!!!
}
我认为,改变的参数值是一个可怕的想法,你不应该这样做,你应该返回包含新修改的新字符串。
这是因为整数是值类型,并通过值而不是通过引用传递。 – 2014-09-25 23:21:07
我使用字符串得到相同的结果。我无法修改ForEach循环内的字符串。 – Ricardo 2014-09-25 23:22:12
@Ricardo真的吗?你正在执行什么操作? – BradleyDotNET 2014-09-25 23:23:01