Изменить переменную Struct в словаре
У меня есть такая структура:
public struct MapTile
{
public int bgAnimation;
public int bgFrame;
}
Но когда я перебираю его с помощью foreach для изменения рамки анимации, я не могу этого сделать...
Здесь код:
foreach (KeyValuePair<string, MapTile> tile in tilesData)
{
if (tilesData[tile.Key].bgFrame >= tilesData[tile.Key].bgAnimation)
{
tilesData[tile.Key].bgFrame = 0;
}
else
{
tilesData[tile.Key].bgFrame++;
}
}
Это дает мне компиляцию:
Error 1 Cannot modify the return value of 'System.Collections.Generic.Dictionary<string,Warudo.MapTile>.this[string]' because it is not a variable
Error 2 Cannot modify the return value of 'System.Collections.Generic.Dictionary<string,Warudo.MapTile>.this[string]' because it is not a variable
Почему я не могу изменить значение внутри структуры, которая находится внутри словаря?
Ответы
Ответ 1
Индексатор вернет копию значения. Внесение изменений в эту копию ничего не сделает со значением в словаре... компилятор останавливает вас от написания багги-кода. Если вы хотите изменить значение в словаре, вам нужно использовать что-то вроде:
// Note: copying the contents to start with as you can't modify a collection
// while iterating over it
foreach (KeyValuePair<string, MapTile> pair in tilesData.ToList())
{
MapTile tile = pair.Value;
tile.bgFrame = tile.bgFrame >= tile.bgAnimation ? 0 : tile.bgFrame + 1;
tilesData[pair.Key] = tile;
}
Обратите внимание, что это также позволяет избежать множественного поиска без уважительных причин, которые выполнял ваш оригинальный код.
Лично я бы настоятельно советовал не иметь сменяемую структуру, чтобы начать с вас...
Конечно, другой вариант - сделать его ссылочным типом, после чего вы можете использовать:
// If MapTile is a reference type...
// No need to copy anything this time; we're not changing the value in the
// dictionary, which is just a reference. Also, we don't care about the
// key this time.
foreach (MapTile tile in tilesData.Values)
{
tile.bgFrame = tile.bgFrame >= tile.bgAnimation ? 0 : tile.bgFrame + 1;
}
Ответ 2
tilesData[tile.Key]
не является местом хранения (т.е. это не переменная). Это копия экземпляра MapTile
, связанного с ключом tile.Key
в словаре tilesData
. Это то, что происходит с struct
. Копии их экземпляров передаются и возвращаются повсюду (и это значительная часть того, почему изменяемая структура считается злой).
Что вам нужно сделать:
MapTile tile = tilesData[tile.Key];
if (tile.bgFrame >= tile.bgAnimation)
{
tile.bgFrame = 0;
}
else
{
tile.bgFrame++;
}
tilesData[tile.Key] = tile;
Ответ 3
Я бы предложил создать класс утилиты:
public class MutableHolder<T>
{
public T Value;
public MutableHolder(T value)
{
this.Value = value;
}
}
Затем сохраните вновь созданный MutableHolder<MapTile>
в каждом слоте словаря, а не сразу сохраните MapTile
. Это позволит вам легко обновить карту, связанную с каким-либо конкретным ключом, без необходимости изменять сам словарь (действие, которое в противном случае, как минимум, аннулирует счетчик, используемый вашим циклом foreach
).
Ответ 4
Chance struct to Class
перед:
public struct MapTile
{
public int bgAnimation;
public int bgFrame;
}
после
public Class MapTile
{
public int bgAnimation;
public int bgFrame;
}