web-dev-qa-db-fra.com

Modifier la variable Struct dans un dictionnaire

J'ai une structure comme celle-ci:

public struct MapTile
{
    public int bgAnimation;
    public int bgFrame;
}

Mais quand je boucle dessus avec foreach pour changer le cadre d'animation, je ne peux pas le faire ...

Voici le code:

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++;
        }
}

Cela me donne un aperçu de la compilation:

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

Pourquoi ne puis-je pas changer une valeur à l'intérieur d'une structure qui se trouve à l'intérieur d'un dictionnaire?

34
NewProger

L'indexeur renverra un copie de la valeur. Apporter une modification à cette copie ne fera rien à la valeur dans le dictionnaire ... le compilateur vous empêche d'écrire du code bogué. Si vous souhaitez modifier la valeur dans le dictionnaire, vous devrez utiliser quelque chose comme:

// 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;
}

Notez que c'est aussi éviter de faire plusieurs recherches sans raison valable, ce que faisait votre code d'origine.

Personnellement, je conseillerais vivement contre d'avoir une structure mutable pour commencer, faites attention ...

Bien sûr, une autre alternative consiste à en faire un type de référence, auquel cas vous pouvez utiliser:

// 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;
}
45
Jon Skeet

tilesData[tile.Key] n'est pas un emplacement de stockage (c'est-à-dire que ce n'est pas une variable). Il s'agit d'une copie de l'instance de MapTile associée à la clé tile.Key dans le dictionnaire tilesData. C'est ce qui se passe avec struct. Des copies de leurs instances sont transmises et retournées partout (et c'est en grande partie pourquoi les structures mutables sont considérées comme mauvaises).

Ce que vous devez faire, c'est:

    MapTile tile = tilesData[tile.Key];
    if (tile.bgFrame >= tile.bgAnimation)
    {
        tile.bgFrame = 0;
    }
    else
    {
        tile.bgFrame++;
    }
    tilesData[tile.Key] = tile;
7
jason

Je suggère de créer une classe utilitaire:

public class MutableHolder<T>
{
    public T Value;
    public MutableHolder(T value)
    {
        this.Value = value;
    }
}

Ensuite, stockez un MutableHolder<MapTile> Nouvellement créé dans chaque emplacement de dictionnaire plutôt que de stocker un MapTile directement. Cela vous permettra de mettre à jour facilement la tuile de carte associée à une clé particulière, sans avoir à modifier le dictionnaire lui-même (un acte qui, sinon, invaliderait au moins l'énumérateur utilisé par votre boucle foreach).

1
supercat

Chance struct en classe

avant:

public struct MapTile
{
    public int bgAnimation;
    public int bgFrame;
}

après:

public Class MapTile
{
    public int bgAnimation;
    public int bgFrame;
}
0
Darlan D.