微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么字典上的 TryGetValue 的键也是返回 Null 的字典?

如何解决为什么字典上的 TryGetValue 的键也是返回 Null 的字典?

目标:从字典中获取一个值。所述值有一个字典作为键。

我在做什么:我正在创建第二个字典,该字典的值与我试图获取其值的键完全相同。使用 TryGetValue

结果:需要一个值但得到空值;

背景: 我正在尝试在 Unity 中制作一个制作功能。这就是制作成分的类的样子(ICombinable 现在看起来完全一样):

public class Ingredient : ICombinable
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public Effect Effect { get; set; }
    }

在实践中,我希望用户能够将 ICombinable 类型的对象拖到 UI(未实现)上,然后按下按钮将它们组合成一个新项目。例如,2 种草药和 1 杯水会返回治疗药水(新物品)。

在表面后面,我将拖动/选择的对象存储在 Dictionary<ICombinable,int> 中,其中 int 是每个 ICombinable数量

在另一个类中,我正在存储另一个字典,它将保存所有的食谱。

public class Combiner
{
        private Dictionary<Dictionary<ICombinable,int>,ICraftable> _recipebook;

        public Combiner()
        {
            _recipebook = new Dictionary<Dictionary<ICombinable,ICraftable>(); // <Recipe,Item it unlocks>
        }

        public void AddRecipe(Dictionary<ICombinable,int> recipe,ICraftable item) =>  _recipebook.Add(recipe,item);
        
        public ICraftable Craft(Dictionary<ICombinable,int> ingredientsAndamount) =>
            _recipebook.TryGetValue(ingredientsAndamount,out var item) == false ? null : item;
        //FirstOrDefault(x => x.Key.requiredComponents.Equals(givenIngredients)).Value;
    }

_recipebook 的关键是由成分及其数量组成的实际配方。 ICraftable 是与该配方对应的对象/项目。在我之前给出的 ICraftable 示例中,将是治疗药水,而 2 根棍子和一杯水将分别是字典中的一个条目,该条目是该值的关键。

最后,Craft 方法需要一个字典(换句话说,一个成分列表及其数量),我希望它在 _recipebook 中检查哪个项目与给定的字典对应。如果成分组合有效,则应返回一个项目,否则返回 null。

我如何测试此功能 我刚开始这个项目,所以我想从单元测试开始。这是设置:

[Test]
    public void combiner_should_return_healing_potion()
    {
        // Use the Assert class to test conditions
        var combiner = new Combiner();
        var item = new Item
        {
            Name = "Healing Potion",Unlocked = false
        };

    
        combiner.AddRecipe(new Dictionary<ICombinable,int>
        {
            {new Ingredient {Name = "Herb",Description = "Has healing properties",Effect = Effect.Heal},3},{new Ingredient {Name = "Water",Description = "Spring water",Effect = default},1},{new Ingredient {Name = "Sugar",Description = "Sweetens",2}
        },item);

        var actualItem = combiner.Craft(new Dictionary<ICombinable,int>
        {
            {new Ingredient { Name = "Herb",2}
        });
        
        Assert.That(actualItem,Is.EqualTo(item));

    }

结果:

combiner_should_return_healing_potion (0.023s)
---
Expected: <Models.Item>
  But was:  null
---

我正在制作一个叫做治疗药水的物品和一本应该是它的配方的字典。我将这些添加到食谱书中。之后,我正在创建第二个字典来“模拟”用户的输入。该词典的内容与我使用 Add 添加到食谱书中的内容完全相同 食谱()。怎么 TryGetValue 不认为这两个字典是平等的?

我该怎么做才能让它发挥作用?

解决方法

因为您要查找的对象在字典中不存在。

您可以很容易地说服自己,只需遍历 Keys 集合并使用 ==Equals 将每个关键字典与搜索到的字典进行比较。

那就是

_recipebook.Count(x => x.Key == ingredientsAndAmount)

您会找到零个匹配项。

解决方案是提供您自己的 EqualsHashCode 实现,或者通过注释中建议的 IEqualityComparer,或者通过将键字典包装到 Recipe提供它们的类,并使用 Recipe 作为实际的键。

,

已解决:

我尝试制作自己的 IEqualitycomparer,但无法让我的字典使用我在其中定义的 Equals。所以我创建了一个 Recipe 类并覆盖了那里的 Equals。

public class Recipe : ICraftable
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public Dictionary<ICombinable,int> RequiredComponents { get; set; }
        public bool Unlocked { get; set; }

        public override bool Equals(object obj)
        {
            var otherDict = obj as Dictionary<ICombinable,int>;
            if (ReferenceEquals(otherDict,RequiredComponents)) return true;
            if (ReferenceEquals(otherDict,null)) return false;
            if (ReferenceEquals(RequiredComponents,null)) return false;
            if (otherDict.GetType() != RequiredComponents.GetType()) return false;
            return otherDict.Count == RequiredComponents.Count && AreValuesEqual(otherDict,RequiredComponents);
        }
        
        private bool AreValuesEqual(Dictionary<ICombinable,int> x,Dictionary<ICombinable,int> y)
        {
            var matches = 0;
            //Goal is to check if all ingredients have the same name on both sides. Then I'll check if they have the same amount
            foreach (var xKvp in x)
            {
                foreach (var yKvp in y)
                {
                    if (xKvp.Key.Name == yKvp.Key.Name && xKvp.Value == yKvp.Value)
                        matches++;
                }
            }
            return matches == x.Count;
        }
    }

我从使用 IEqualityComparer 获得的默认 Equals 中复制了前 4 行,并为第 5 行自定义了一行。我的 AreValuesEqual bool 只是通过名称和计数检查其他字典中是否存在所有成分。

我更改了我的 Combiner 类,使用 Recipe 对象作为键而不是 Dictionary 并相应地调整了 AddRecipe 和 Craft 的方法:

public class Combiner
    {
        private Dictionary<Recipe,ICraftable> _recipebook;

        public Combiner()
        {
            _recipebook = new Dictionary<Recipe,ICraftable>(); // <Recipe,Item it unlocks>
        }

        public void AddRecipe(Recipe recipe,ICraftable item) =>  _recipebook.Add(recipe,item);
        
        public ICraftable Craft(Dictionary<ICombinable,int> ingredientsAndAmount) =>
            _recipebook.FirstOrDefault(kvp=> kvp.Key.Equals(ingredientsAndAmount)).Value;
    }

这就是我设置单元测试的方式:

[Test]
    public void combiner_should_return_healing_potion()
    {
        // Use the Assert class to test conditions
        var combiner = new Combiner();
        var potion = new Item
        {
            Name = "Healing Potion",Unlocked = false
        };

        combiner.AddRecipe(new Recipe
            {
                Name = "Healing potion recipe",Description = "Invented by some sage",RequiredComponents = new Dictionary<ICombinable,int>
                {
                    {new Ingredient() { Name = "Herb",Description = "Has healing properties",Effect = Effect.Heal},3},{new Ingredient {Name = "Water",Description = "Spring water",Effect = default},1},{new Ingredient {Name = "Sugar",Description = "Sweetens",2}

                }
            },potion);

        var userGivenIngredientsAndAmount = combiner.Craft(new Dictionary<ICombinable,int>()
        {
            {new Ingredient() { Name = "Herb",2}
        });
        
        Assert.That(userGivenIngredientsAndAmount,Is.EqualTo(potion));
    }

它正在按预期运行。更改其中一个字典中的名称或计数会导致它像它应该的那样返回 null。这可能是非常低效的!但我仍处于“让它发挥作用”的阶段。我很快就会“让它变得更好,让它变得更快”。

感谢大家让我走上正轨!我很乐意接受任何关于提高效率的建议。但由于它按预期工作,我将这个问题标记为明天已解决。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。