andersop andersop - 3 months ago 13
C# Question

c# modifying structs in a List<T>

Short question: How can I modify individual items in a

List
? (or more precisely, members of a
struct
stored in a
List
?)

Full explanation:

First, the
struct
definitions used below:

public struct itemInfo
{
...(Strings, Chars, boring)...
public String nameStr;
...(you get the idea, nothing fancy)...
public String subNum; //BTW this is the element I'm trying to sort on
}

public struct slotInfo
{
public Char catID;
public String sortName;
public Bitmap mainIcon;
public IList<itemInfo> subItems;
}

public struct catInfo
{
public Char catID;
public String catDesc;
public IList<slotInfo> items;
public int numItems;
}

catInfo[] gAllCats = new catInfo[31];


gAllCats
is populated on load, and so on down the line as the program runs.

The issue arises when I want to sort the
itemInfo
objects in the
subItems
array.
I'm using LINQ to do this (because there doesn't seem to be any other reasonable way to sort lists of a non-builtin type).
So here's what I have:

foreach (slotInfo sInf in gAllCats[c].items)
{
var sortedSubItems =
from itemInfo iInf in sInf.subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
sInf.subItems.Clear();
sInf.subItems = sortedSubTemp; // ERROR: see below
}


The error is, "Cannot modify members of 'sInf' because it is a 'foreach iteration variable'".

a, this restriction makes no sense; isn't that a primary use of the foreach construct?

b, (also out of spite) what does Clear() do if not modify the list? (BTW, the List does get cleared, according to the debugger, if I remove the last line and run it.)

So I tried to take a different approach, and see if it worked using a regular for loop. (Apparently, this is only allowable because
gAllCats[c].items
is actually an
IList
; I don't think it will allow you to index a regular
List
this way.)

for (int s = 0; s < gAllCats[c].items.Count; s++)
{
var sortedSubItems =
from itemInfo iInf in gAllCats[c].items[s].subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo>();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
//NOTE: the following two lines were incorrect in the original post
gAllCats[c].items[s].subItems.Clear();
gAllCats[c].items[s].subItems = sortedSubTemp; // ERROR: see below
}


This time, the error is, "Cannot modify the return value of 'System.Collections.Generic.IList.this[int]' because it is not a variable." Ugh! What is it, if not a variable? and when did it become a 'return value'?

I know there has to be a 'correct' way to do this; I'm coming to this from a C background and I know I could do it in C (albeit with a good bit of manual memory management.)

I searched around, and it seems that
ArrayList
has gone out of fashion in favor of generic types (I'm using 3.0) and I can't use an array since the size needs to be dynamic.

Answer

Looking at the for-loop approach, the reason (and solution) for this is given in the documentation for the compilation error:

An attempt was made to modify a value type that is produced as the result of an intermediate expression but is not stored in a variable. This error can occur when you attempt to directly modify a struct in a generic collection.

To modify the struct, first assign it to a local variable, modify the variable, then assign the variable back to the item in the collection.

So, in your for-loop, change the following lines:

catSlots[s].subItems.Clear();
catSlots[s].subItems = sortedSubTemp;   // ERROR: see below

...into:

slotInfo tempSlot = gAllCats[0].items[s];
tempSlot.subItems  = sortedSubTemp;
gAllCats[0].items[s] = tempSlot;

I removed the call to the Clear method, since I don't think it adds anything.

Comments