在C#中的List中更新结构的属性

本文关键字:结构 属性 更新 中的 List | 更新日期: 2023-09-27 18:22:19

我有一个List<Cat>,其中Cat是一个有Id和Name的struct

如何更改id为7的猫的名称?

我不假思索地做了

var myCat = catList.Single(c => c.Id == 7);
mycat.Name = "Dr Fluffykins";

当然,structs是值类型。那么,是否可以使用这样的技术,或者我必须将.Single更改为for循环并存储索引以用更新的结构替换它?

在C#中的List中更新结构的属性

由于struct是值类型,并且值类型会被复制,因此myCat最终会从列表中复制cat。您需要对struct本身进行操作,而不是对其副本进行操作。

此外,只有当struct的字段是单个变量或数组的一部分时,才能直接修改它们。List<T>的索引器返回一个副本,因此C#编译器会产生"无法修改值类型"错误。

我知道的唯一解决方案(除了将Cat设为class或重新分配修改后的副本之外)是将catList设为阵列:

var indexOf = catArray
    .Select((Cat, Index) => new {Cat, Index})
    .Single(p => p.Cat.Id == 7).Index;
catArray[indexOf].Name = "Dr Fluffykins";

要更新ID为7的列表中项目的值,必须首先找到它在列表中的位置。一旦其索引已知,就可以使用适用于所有暴露场结构的正常方法对其进行更新:

int index = myList.Find( it => it.ID == 7);
if (index >= 0)
{
  var temp = myList[index];
  temp.Name = "Dr Fluffykins";
  myList[index] = temp;
}

请注意,有些人认为structs不如class,因为上面的代码如果没有最后一行就永远无法工作。我认为相反:在许多情况下,它们是优越的,因为仅仅知道某个东西是一个公开的字段结构就足以知道最后一行是必要的和充分的。相比之下,如果所讨论的类型是一个可变类,那么无论是否有最后一行,代码都可能按预期工作,或者可能会产生意外的副作用。

顺便说一句,如果经常使用Id,我建议使用Dictionary<int, Cat>而不是List<Cat>。或者,您可以使用Cat[]Dictionary<int,int>来跟踪阵列中不同猫的位置。后一种方法的一个优点是,与List<Cat>不同,Cat[]可以简单地说:

myArray[index].Name = "Dr Fluffykins";

以直接更新数组中的项。这样的方法非常高效。或者,您可以编写一个类似List<T>的类,它包括一个方法"ActOnItem(int index, ref T it)ActOnItem<TParam1>(int index, ref T it, ref TParam1 param1)方法,可以调用myList.ActOnItem(index, (ref Cat it)=>it.Name = "Dr Fluffykins");,或者——如果theNewName是一个变量,myList.ActOnItem(index, (ref Cat it, ref string TheNewName)=>it.Name = theNewName);注意,可以将theNewName存储到项中,而不将其作为ref或非ref参数传递,但关闭局部变量的lambda比不关闭局部变量慢得多。