c # изменение структур в списке <T>

Короткий вопрос: как я могу изменить отдельные элементы вList? (или, точнее, членыstruct хранится вList?)

Полное объяснение:

Во-первых,struct определения, используемые ниже:

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 заполняется при загрузке и т. д. по ходу программы.

Проблема возникает, когда я хочу отсортироватьitemInfo объекты вsubItems массив. Я использую для этого LINQ (поскольку, похоже, нет другого разумного способа сортировки списков не встроенного типа). Вот что у меня есть:

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
}

Ошибка заключается в том, что «Невозможно изменить элементы« sInf »; потому что это «переменная итерации foreach».

а, это ограничение не имеет смысла; разве это не первичное использование конструкции foreach?

b, (также не зря), что делает Clear (), если не изменяет список? (Кстати, список очищается, по словам отладчика, если я удалю последнюю строку и запусту ее.)

Поэтому я попытался использовать другой подход и посмотреть, работает ли он, используя обычный цикл for. (Видимо, это допустимо только потому, чтоgAllCats[c].items на самом делеIList; Я не думаю, что это позволит вам индексировать регулярныеList сюда.)

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
}

На этот раз ошибка: «Невозможно изменить возвращаемое значение» System.Collections.Generic.IList.this [int] & apos; потому что это не переменная. & quot; Тьфу! Что это, если не переменная? и когда он стал «возвращаемым значением»?

Я знаю, что должен быть «правильный» способ сделать это; Я пришел к этому из C-фона, и я знаю, что мог бы сделать это в C (хотя и с хорошим ручным управлением памятью.)

Я искал вокруг, и кажется, чтоArrayList вышла из моды в пользу универсальных типов (я использую 3.0), и я не могу использовать массив, так как размер должен быть динамическим.

Ответы на вопрос(5)

Решение Вопроса

документация для ошибки компиляции:

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.

Итак, в вашем цикле for измените следующие строки:

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

...в:

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

Я убрал звонок наClear метод, так как я не думаю, что это что-то добавляет.

 andersop01 июл. 2009 г., 08:01
Здорово, это работает. Большое спасибо!
 26 окт. 2009 г., 17:31
Спасибо за указание на это. Я знал об этом так же, как об изменении свойства свойства struct, но я не знал, что это применимо к универсальным коллекциям. Я просто предполагал, что сам список был классом, поэтому индексатор возвращал бы ссылку на структуру. Я представляю, как это работает с неуниверсальными коллекциями. еще раз спасибо
 14 дек. 2009 г., 16:30
Просто нашел это решение. Работает - спасибо!

foreach в том, что структуры являются типами значений, и в результате переменная итерации цикла на самом деле не является ссылкой на структуру в списке, а скорее является копией структуры.

Я думаю, что компилятор запрещает вам изменять его, потому что он, скорее всего, не будет делать то, что вы ожидаете в любом случае.

subItems.Clear() это меньше проблем, потому что хотя поле может быть копией элемента в списке, оно также является ссылкой на список (мелкая копия).

Самым простым решением, вероятно, было бы изменитьstruct кclass за это. Или использовать совершенно другой подход сfor (int ix = 0; ix < ...; ix++), так далее.

 andersop01 июл. 2009 г., 07:27
Есть ли способ сделать foreach () с ссылочным типом? Кроме того, re: & quot; для & quot; подход, я попробовал это, как отмечалось ... другая ошибка, хотя.
 01 июл. 2009 г., 16:00
Классы являются ссылочными типами ... классы могут использоваться в foreach ... так что да ... (не уверен, что вызвало этот вопрос) ... и извините за недостаточную детализацию для & apos; for & apos; подход ... Я не должен был уделить достаточно внимания вашему вопросу. Я вижу, что Фредрик уже разъяснил, хотя.

ЕслиsubItems был изменен на конкретный список вместо интерфейсаIListто вы сможете использоватьSort метод.

public List<itemInfo> subItems;

Таким образом, весь ваш цикл становится:

foreach (slotInfo sInf in gAllCats[c].items)
    sInf.subItems.Sort();

Это не потребует содержанияstruct быть измененным вообще (вообще хорошая вещь).structУчастники по-прежнему будут указывать на одни и те же объекты.

Кроме того, очень мало веских причин для использованияstruct в C #. GC очень, очень хорош, и вам будет лучше сclass пока вы не продемонстрировали узкое место выделения памяти в профилировщике.

Еще лаконичнее, еслиitems вgAllCats[c].items такжеList, ты можешь написать:

gAllCats[c].items.ForEach(i => i.subItems.Sort()),;

Edit: ты сдаешься слишком легко! :)

Sort очень легко настроить. Например:

var simpsons = new[]
               {
                   new {Name = "Homer", Age = 37},
                   new {Name = "Bart", Age = 10},
                   new {Name = "Marge", Age = 36},
                   new {Name = "Grandpa", Age = int.MaxValue},
                   new {Name = "Lisa", Age = 8}
               }
               .ToList();

simpsons.Sort((a, b) => a.Age - b.Age);

Это от самого младшего к старшему. (Разве не подходит вывод типа в C # 3?)

 andersop01 июл. 2009 г., 11:11
Затем я должен определить, что означает сортировка этих объектов - я действительно хочу отсортировать по одному конкретному элементу. Я полагаю, что это может привести к перегрузке метода Sort () для itemInfo, но документы действительно ужасны в отношении подобных вещей. Я фактически сделал gAllCats массивом, потому что я не мог понять, как им манипулировать, если бы использовал List. Тьфу .... Во всяком случае, спасибо.

что вы используете структуры, которые в C # являются типами значений, а не ссылочными типами.

Вы абсолютно можете использовать ссылочные типы в циклах foreach. Если вы измените свои структуры на классы, вы можете просто сделать это:

    foreach(var item in gAllCats[c].items)
    {
        item.subItems = item.subItems.OrderBy(x => x.subNum).ToList();
    }

Со структурами это должно было бы измениться на:

    for(int i=0; i< gAllCats[c].items.Count; i++)
    {
        var newitem = gAllCats[c].items[i];
        newitem.subItems = newitem.subItems.OrderBy(x => x.subNum).ToList();
        gAllCats[c].items[i] = newitem;
    }

Другие ответы содержат более подробную информацию о том, почему структуры работают не так, как классы, но я подумал, что смогу помочь с сортировкой.

sInf является копией структуры внутри элементов. измененияsInf не изменит "фактический" структура в списке.

Очистка работает, потому что вы не меняете sInf, вы меняете список внутри sInf иIlist<T> всегда будет ссылочным типом.

То же самое происходит, когда вы используете оператор индексации наIList<T> - он возвращает копию вместо фактической структуры. Если компилятор разрешилcatSlots[s].subItems = sortedSubTemp;Вы будете изменять подэлементы копии, а не фактическую структуру. Теперь вы понимаете, почему компилятор говорит, что возвращаемое значение не является переменной - на копию нельзя ссылаться снова.

Существует довольно простое исправление - работайте с копией, а затем перезаписывайте исходную структуру своей копией.

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);
            }
            var temp = catSlots[s];
            temp.subItems = sortedSubTemp;
            catSlots[s] = temp;
}

Да, это приводит к двум операциям копирования, но это цена, которую вы платите за семантику значения.

 andersop01 июл. 2009 г., 07:58
Ах, индексирование [] на самом деле также является копией, но только если оно находится в списке - для массива это тип значения. Полезно знать, спасибо.

Ваш ответ на вопрос