I have a List<Cat>
where Cat
is a struct
that has an Id and a Name.
How do I change the name of the cat with id 7?
I did (without thinking)
var myCat = catList.Single(c => c.Id == 7);
mycat.Name = "Dr Fluffykins";
But of course, structs are value types. So is it possible to use a technique like this, or do I have to change .Single
into a for
loop and store the index to replace it with the updated struct?
To update the value of the item in the list whose ID is seven, one must first find its location within the list. Once its index is known, one can update it using the normal approach which is applicable to all exposed-field structures:
int index = myList.Find( it => it.ID == 7);
if (index >= 0)
{
var temp = myList[index];
temp.Name = "Dr Fluffykins";
myList[index] = temp;
}
Note that some people think structs are inferior to classes because the above code will never work without the last line. I would argue the opposite: in many cases, they're superior because merely knowing that something is an exposed-field struct is sufficient to know that the last line will be necessary and sufficient. By contrast, if the type in question were a mutable class, the code, with or without the last line, might work as expected or might have unintended side-effects.
Incidentally, if one will be using Id
a lot, I would suggest using a Dictionary<int, Cat>
rather than a List<Cat>
. Alternatively, you could use a Cat[]
along with a Dictionary<int,int>
to keep track of the locations of different cats within the array. One advantage of this latter approach is that with a Cat[]
, unlike a List<Cat>
, one can simply say:
myArray[index].Name = "Dr Fluffykins";
to update the item in the array directly. Such an approach is very performant. Alternatively, you could write a List<T>
-like class which includes a methods "ActOnItem(int index, ref T it)
and ActOnItem<TParam1>(int index, ref T it, ref TParam1 param1)
method, which could be invoked myList.ActOnItem(index, (ref Cat it)=>it.Name = "Dr Fluffykins");
or--if theNewName
is a variable, myList.ActOnItem(index, (ref Cat it, ref string TheNewName)=>it.Name = theNewName);
Note that one could store theNewName
to the item without passing it as a ref
or non-ref parameter, but lambdas which close local variables are much slower than those which don't.
Since struct
s are value types, and because value types get copied, myCat
ends up with the copy of the cat from the list. You need to operate on the struct
itself, not its copy.
Moreover, you can modify fields of struct
s directly only when they are single variables or parts of an array. List<T>
's indexer returns a copy, so C# compiler produces the "Cannot modify a value type" error.
The only solution that I know (short of making Cat
a class
or re-assigning a modified copy) is making catList
an array:
var indexOf = catArray
.Select((Cat, Index) => new {Cat, Index})
.Single(p => p.Cat.Id == 7).Index;
catArray[indexOf].Name = "Dr Fluffykins";
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With