MyList.Addnew MyStruct"peter";
现在我要更改一个元素:
1MyList1.Name ="bob"
但是,每当"/>

关于c#:更改结构列表中元素的值

关于c#:更改结构列表中元素的值

Changing the value of an element in a list of structs

我有一个结构列表,我想更改一个元素。例如:

1
2
MyList.Add(new MyStruct("john");
MyList.Add(new MyStruct("peter");

现在我要更改一个元素:

1
MyList[1].Name ="bob"

但是,每当我尝试执行此操作时,都会出现以下错误:

Cannot modify the return value of
System.Collections.Generic.List.this[int]‘ because it is not
a variable

如果我使用类列表,则不会发生此问题。

我想答案与结构是值类型有关。

因此,如果我有一个结构列表,我应该将它们视为只读吗?如果我需要更改列表中的元素,那么应该使用类而不是结构?


不完全是。将类型设计为类或结构不应由将其存储在集合中的需求驱动:)您应该查看所需的"语义"

您看到的问题是由于值类型语义所致。每个值类型变量/引用都是一个新实例。当你说

1
Struct obItem = MyList[1];

发生的事情是创建了该结构的新实例,并且所有成员都被一个一个地复制。这样您就可以克隆MyList [1],即2个实例。
现在,如果修改obItem,它不会影响原始对象。

1
obItem.Name ="Gishu";  // MyList[1].Name still remains"peter"

现在在这里和我一起忍受2分钟(这需要花一会儿时间才能吞下..对我有用:)
如果确实需要将结构存储在集合中并按照问题中的指示进行修改,则必须使结构公开一个接口(但是这将导致装箱)。然后,您可以通过引用盒装对象的接口引用来修改实际的结构。

以下代码段说明了我在上面刚刚说过的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface IMyStructModifier
{
    String Name { set; }
}
public struct MyStruct : IMyStructModifier ...

List<Object> obList = new List<object>();
obList.Add(new MyStruct("ABC"));
obList.Add(new MyStruct("DEF"));

MyStruct temp = (MyStruct)obList[1];
temp.Name ="Gishu";
foreach (MyStruct s in obList) // =>"ABC","DEF"
{
    Console.WriteLine(s.Name);
}

IMyStructModifier temp2 = obList[1] as IMyStructModifier;
temp2.Name ="Now Gishu";
foreach (MyStruct s in obList) // =>"ABC","Now Gishu"
{
    Console.WriteLine(s.Name);
}

HTH。好问题。
更新:@Hath-您要我跑步检查我是否忽略了这么简单的内容。 (如果没有setter属性而方法没有做到这一点-.Net宇宙仍然是平衡的:)
设置器方法不起作用
obList2 [1]返回其状态将被修改的副本。列表中的原始结构保持不变。因此,"通过接口设置"似乎是唯一的方法。

1
2
3
4
5
6
7
8
List<MyStruct> obList2 = new List<MyStruct>();
obList2.Add(new MyStruct("ABC"));
obList2.Add(new MyStruct("DEF"));
obList2[1].SetName("WTH");
foreach (MyStruct s in obList2) // =>"ABC","DEF"
{
    Console.WriteLine(s.Name);
}

1
MyList[1] = new MyStruct("bob");

C#中的结构几乎应始终设计成不可变的(也就是说,一旦创建,便无法更改其内部状态)。

在您的情况下,您要做的是替换指定数组索引中的整个结构,而不是尝试仅更改单个属性或字段。


结构"不可变"不是很多。

真正的潜在问题是结构是值类型,而不是引用类型。因此,当您从列表中拉出对该结构的"引用"时,它将创建整个结构的新副本。因此,您对此所做的任何更改都将更改副本,而不是列表中的原始版本。

就像安德鲁指出的那样,您必须替换整个结构。尽管如此,我认为您必须问自己,为什么首先要使用结构(而不??是类)。确保您没有围绕过早的优化问题进行操作。


具有公开字段或允许通过属性设置器进行突变的结构没有问题。但是,响应于方法或属性获取器而发生变异的结构很危险,因为系统允许在临时结构实例上调用方法或属性获取器。如果方法或获取方法更改了结构,则这些更改最终将被丢弃。

不幸的是,正如您所注意到的,.net内置的集合在暴露其中包含的值类型对象方面确实微不足道。最好的选择通常是执行以下操作:

1
2
3
  MyStruct temp = myList[1];
  temp.Name ="Albert";
  myList[1] = temp;

有点令人讨厌,而且根本不是线程安全的。仍然是对类类型的列表的一种改进,在这种情况下,可能需要执行以下操作:

1
  myList[1].Name ="Albert";

,但可能还需要:

1
  myList[1] = myList[1].Withname("Albert");

或者也许

1
2
3
  myClass temp = (myClass)myList[1].Clone();
  temp.Name ="Albert";
  myList[1] = temp;

或其他一些变化。除非一个人检查了myClass以及将这些东西放到列表中的其他代码,否则一个人实际上是不会知道的。如果不检查无法访问的程序集中的代码,则很可能无法知道第一种形式是否安全。相比之下,如果Name是MyStruct的公开字段,那么我给出的用于更新它的方法将起作用,而不管MyStruct包含的内容是什么,也不管代码执行前myList可能执行的其他操作或期望的代码是什么之后再做。


除了其他答案,我认为解释编译器抱怨的原因可能会有所帮助。

当您调用MyList[1].Name时,与数组不同,MyList[1]实际上是在幕后调用indexer方法。

每当一个方法返回一个结构的实例时,您都将获得该结构的副本(除非您使用ref / out)。

因此,您将获得一个副本,并在副本上设置Name属性,由于该副本未存储在任何地方的变量中,因此该属性将被丢弃。

本教程更详细地描述了正在发生的事情(包括生成的CIL代码)。


推荐阅读

    linux目录结构树命令?

    linux目录结构树命令?,系统,工作,信息,数据,设备,管理,目录,发展,时间,结构,L

    linux磁盘列表命令?

    linux磁盘列表命令?,情况,管理,系统,单位,信息,数据,命令,磁盘,服务,时间,lin

    linux命令更改时区?

    linux命令更改时区?,时间,系统,标准,大陆,国家,命令,时区,终端,之后,指令,lin

    linux查询表结构命令?

    linux查询表结构命令?,系统,标准,信息,数据,地址,设备,时间,适当,软件,命令,l

    linux命令筛选列表?

    linux命令筛选列表?,工具,状态,位置,工作,预期,命令,名称,标准,数据,系统,在L

    linux命令更改为中文?

    linux命令更改为中文?,系统,单位,地方,软件,管理,标准,工具,信息,设备,中文,m

    linux命令更改域名?

    linux命令更改域名?,地址,网站,系统,名称,服务,软件,环境,网址,信息,实时,Lin

    linux的长列表命令?

    linux的长列表命令?,工作,系统,信息,命令,数据,目录,电脑,软件,时间,设备,Lin

    linux命令更改端口号?

    linux命令更改端口号?,服务,系统,邮箱,通用,软件,第三,位置,端口,文件,编辑,

    linux更改帐号命令?

    linux更改帐号命令?,密码,系统,用户,命令,第三,信息,代码,电脑,地址,终端,lin

    linux更改用户名命令?

    linux更改用户名命令?,系统,密码,用户,网上,代码,命令,用户名,主机名,终端,

    linux命令行更改ip?

    linux命令行更改ip?,地址,系统,代码,密码,信息,网络,命令,终端,方法,下一步,l

    linux下更改权限命令?

    linux下更改权限命令?,系统,档案,数字,权限,文件,命令,人员,密码,新增,目录,l

    linux目录列表命令?

    linux目录列表命令?,系统,信息,标准,工作,命令,地址,时间,数据,名称,目录,lin

    linux命令更改时间?

    linux命令更改时间?,时间,系统,工作,信息,命令,工具,环境,服务,代码,名称,在L

    linux命令行更改时区?

    linux命令行更改时区?,时间,系统,标准,国家,大陆,时区,命令,资料,网上,终端,l

    linux更改属组命令?

    linux更改属组命令?,系统,文件,用户组,命令,名称,所有者,目录,用户,终端,用

    linux下更改账户命令?

    linux下更改账户命令?,密码,系统,用户,信息,第三,命令,代码,管理,名称,软件,l

    linux顺序结构命令?

    linux顺序结构命令?,位置,电脑,资料,情况,系统,环境,有限,名字,地址,标准,fat