web-dev-qa-db-ja.com

辞書の構造体変数を変更する

私はこのような構造体を持っています:

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

辞書内にある構造体内の値を変更できないのはなぜですか?

34
NewProger

インデクサーは、値のcopyを返します。そのコピーに変更を加えても、ディクショナリ内の値は何も変更されません...コンパイラーはバグのあるコードの作成を阻止します。辞書の値を変更したい場合は、次のようなものを使用する必要があります。

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

これがalsoであることに注意してください。元のコードが行っていた正当な理由なく、複数のルックアップを回避することができます。

個人的に私は強くお勧めしますに対して最初に変更可能な構造体を持っていることを覚えておいてください...

もちろん、もう1つの方法は、それを参照型にすることです。その時点で、次のように使用できます。

// 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]は格納場所ではありません(つまり、変数ではありません)。辞書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;
7
jason

ユーティリティクラスを作成することをお勧めします。

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

次に、新しく作成したMutableHolder<MapTile>MapTileを直接格納するのではなく、各辞書スロットに挿入します。これにより、ディクショナリ自体を変更することなく、特定のキーに関連付けられたマップタイルを簡単に更新できます(そうしないと、少なくとも、foreachループで使用される列挙子が無効になります)。

1
supercat

クラスへのチャンス構造

前:

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

後:

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