修改字典中的结构变量

Posted

技术标签:

【中文标题】修改字典中的结构变量【英文标题】:Modify Struct variable in a Dictionary 【发布时间】:2021-12-04 18:44:32 【问题描述】:

我有一个这样的结构:

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

为什么我不能更改字典中的结构中的值?

【问题讨论】:

Ewww...可变结构。 (有人不得不说) 你没有在这里使用课程有什么原因吗? @Justin Niessner,为什么不好? @Lurler - 因为它们会导致大量混乱。人们开始像使用类一样使用它们,并期望它们的行为也与类相同……但事实并非如此(这就是您的代码存在问题的原因)。 @Lurler:这正是可变结构的问题——行为是有问题的。大多数时候,出于性能原因选择结构是过早的优化。最好将您的代码编写成可用和可维护的,然后进行分析,然后在必要时进行优化。在这种情况下,我怀疑这实际上会更快,尤其是如果它存储在字典中。 【参考方案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;

【讨论】:

...这就是我发表评论而不是答案的原因。我知道当可变结构的话题出现时,Jon Skeet 也不会落后。 啊,现在我知道是什么问题了。感谢您的帮助! @JustinNiessner 不管怎样,Jon Skeet 提供了一个非常好的和简洁的答案!【参考方案2】:

tilesData[tile.Key] 不是存储位置(即,它不是变量)。它是与字典 tilesData 中的键 tile.Key 关联的 MapTile 实例的副本。这就是struct 发生的情况。它们的实例的副本到处传递并返回(这也是为什么可变结构被认为是邪恶的很大一部分)。

你需要做的是:

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

【讨论】:

我认为结构不是引用类型,而是变量类型......奇怪。谢谢你的解释。 现在我也明白了为什么它们被认为是邪恶的 :) 我可能会在未来改变它的工作方式 :) struct 是值类型。值类型的显着特点是它们是按值复制的,相等是由值相等定义的。【参考方案3】:

我建议创建一个实用程序类:

public class MutableHolder<T>

    public T Value;
    public MutableHolder(T value)
    
        this.Value = value;
    

然后将新创建的MutableHolder&lt;MapTile&gt; 存储到每个字典槽中,而不是直接存储MapTile。这将允许您轻松更新与任何特定键关联的地图图块,而无需修改字典本身(否则,至少会使 foreach 循环使用的枚举器无效)。

【讨论】:

框架中是否已经有什么是我现在应该使用的通用 MutableHolder 了?我主要是自己创建的。 @MichaelCovelli:可以使用单元素数组,当然一开始就可以使用,但我不知道是否存在任何与上述等效的标准框架实用程序类。另一方面,MutableHolder 足够简单,使用自己的没有太多缺点。【参考方案4】:

机会结构到类

之前:

public struct MapTile

    public int bgAnimation;
    public int bgFrame;

之后:

public Class MapTile

    public int bgAnimation;
    public int bgFrame;

【讨论】:

以上是关于修改字典中的结构变量的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Alamofire 获取请求中调用包含变量或常量的结构到字典中

Swift 如何访问 Struct 对象字典中的特定变量

程序通过定义学生结构体变量,存储学生的学号姓名和3门课的成绩。函数fun的功能是:对形参b所指结构体变量中的数据进行修改,并在主函数中输出修改后的数据。

python中的数据结构

C语言如何修改const结构体内的变量

python实用小技之数据结构