Подтвердить что ты не робот

Изменить переменную 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

Почему я не могу изменить значение внутри структуры, которая находится внутри словаря?

4b9b3361

Ответ 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;
}