为何在列表中迭代C#结构体时无法修改其属性?
为什么遍历结构体列表时无法修改属性?
这个问题的核心其实和C#里**值类型(Value Type)**的特性直接相关,咱们一步步把它说清楚:
先搞懂结构体的本质:值类型的拷贝特性
C#里的结构体是值类型,和类(引用类型)最大的区别就是:值类型在赋值、传递或者从集合中获取时,会创建整个实例的副本,而不是像引用类型那样只传递内存地址。这个特性是所有问题的根源。
为什么foreach循环里改不了?
在你的foreach代码里:
foreach(var l in list) { l.Prop = 20; // 编译错误 }
这里的l是列表中对应结构体元素的副本,而且C#编译器特意把这个迭代变量设为了只读状态——就算不设只读,你修改l的属性也只是改了这个副本,原列表里的结构体元素完全不会受到影响,属于无效操作。编译器干脆直接禁止这种无意义的代码,就抛出了那个“Cannot modify members of 'l' because it is a 'foreach iteration variable'"的错误。
为什么for循环里也改不了?
你可能以为换成for循环就能绕过去,但其实问题本质没变:
for (int i=0; i<list.Count(); ++i) list[i].Prop = 20; // 还是编译错误
这里的list[i]调用的是List<T>的索引器,它返回的依然是结构体的临时副本。你修改这个副本的Prop属性后,这个副本马上就会被丢弃,原列表里的结构体元素根本没被改动。编译器同样能识别出这种无效操作,所以也会报错。
那怎么才能正确修改呢?
要解决这个问题,你需要手动完成“取出副本→修改副本→把修改后的副本放回列表”的流程:
for (int i = 0; i < list.Count; ++i) { var temp = list[i]; // 取出列表中结构体的副本 temp.Prop = 20; // 修改副本的属性 list[i] = temp; // 将修改后的副本重新放回列表,替换原元素 }
这样操作后,列表里的结构体元素才会真正被更新。
补充:如果是类(引用类型)就不会有这个问题
如果把A改成类而不是结构体,不管是foreach还是for循环,修改属性都不会报错——因为列表里存的是类实例的引用,你操作的是引用指向的原实例,而不是副本。
内容的提问来源于stack exchange,提问作者Grigoryants Artem
相关产品推荐
相关产品推荐

