按列表和数组中的索引获取结构项

本文关键字:索引 获取 结构 列表 数组 | 更新日期: 2023-09-27 18:25:36

当我使用structs的数组(例如System.Drawing.Point)时,我可以按索引获取项目并对其进行更改。

例如(此代码工作正常):

Point[] points = new Point[] { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Length; i++)
{
    points[i].X += 1;
}

但当我使用List时,它不起作用:

无法修改的返回值'System.Collections.Generic.List.this[int]'因为它不是可变

示例(此代码无法正常工作):

List<Point> points = new List<Point>  { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++)
{
    points[i].X += 1;
}

我知道,当我逐个索引获取列表项时,我会得到它的副本,编译器会提示我没有犯错误,但为什么要获取数组索引的元素呢?

按列表和数组中的索引获取结构项

这是因为对于数组points[i]显示对象所在的位置。换句话说,数组的points[i]基本上是内存中的指针。因此,您可以对内存中的记录执行操作,而不是对某些副本执行操作。

List<T>的情况并非如此:它在内部使用数组,但通过方法进行通信,导致这些方法将值从内部数组中复制出来,显然修改这些副本没有多大意义:您会立即忘记它们,因为您没有将副本写回内部数组。

正如编译器所建议的,解决这个问题的方法是:

(3,11): error CS1612: Cannot modify a value type return value of `System.Collections.Generic.List<Point>.this[int]'. Consider storing the value in a temporary variable

因此,您可以使用以下"技巧":

List<Point> points = new List<Point>  { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++) {
    Point p = points[i];
    p.X += 1;
    points[i] = p;
}

因此,您将副本读取到一个临时变量中,修改副本,并将其写回List<T>

原因是结构是值类型,因此当您访问列表元素时,实际上您将访问该元素的中间副本,该副本已由列表的索引器返回。

换句话说,当使用List<T>时,您正在创建副本。

MSDN

错误消息

无法修改"expression"的返回值,因为它不是可变

试图修改的值类型是中间表达。由于该值未持久化将保持不变。

若要解决此错误,请将表达式的结果存储在中间值,或使用中间值的引用类型表示

解决方案:

List<Point> points = new List<Point>  { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++)
{
    Point p = points[i];
    p.X += 1;
    //and don't forget update the old value, because we're using copy
    points[i] = p;
}