KeyNotFoundException: данный ключ отсутствовал в словаре

#c# #.net #dictionary #keynotfoundexception

#c# #.net #словарь #keynotfoundexception

Вопрос:

Я получаю KeyNotFoundException всякий раз, когда пытаюсь получить доступ к ключу в словаре.

У меня есть PricingRules класс, в котором есть словарь:

 public class PricingRules
{
    Dictionary<ItemCode, Money> pricing_rules;

    public PricingRules()
    {
        pricing_rules = new Dictionary<ItemCode, Money>();
        pricing_rules.Add(new ItemCode("A"), new Money(50));
        pricing_rules.Add(new ItemCode("B"), new Money(30));
        pricing_rules.Add(new ItemCode("C"), new Money(20));
        pricing_rules.Add(new ItemCode("D"), new Money(15));
    }

    public void itemScanned(ItemCode itemCode, Money amount)
    {
        amount.Add(pricing_rules[itemCode]);
    }

    public Money AmountForItem(ItemCode itemCode)
    {
        return pricing_rules[itemCode];
    }
}
  

Ключ в словаре имеет тип ItemCode :

 public class ItemCode
{
    private readonly string itemCode;

    public ItemCode(string itemCode)
    {
        this.itemCode = itemCode;
    }

    public override int GetHashCode()
    {
        return itemCode.GetHashCode();
    }
}
  

и значение имеет тип Money :

 public class Money
{
    private int amount;

    public Money(int amount)
    {
        this.amount = amount;
    }

    public override bool Equals(object obj)
    {
        Money money = (Money)obj;
        return this.amount == money.amount;
    }

    public void Add(object obj)
    {
        Money money = (Money)obj;
        this.amount  = money.amount;
    }

    public override int GetHashCode()
    {
        return amount.GetHashCode();
    }
}
  

Я попытался переопределить GetHashCode() функцию, чтобы вернуть хэш-код примитивного типа в классе, но он по-прежнему выдает KeyNotFoundException

Тест, который я написал, является:

 public class PricingRulesShould
{
    [Fact]
    void ReturnValueFiftyWhenKeyIsA()
    {
        Money expected = new Money(50);
        PricingRules pr = new PricingRules();

        ItemCode itemcode = new ItemCode("A");
        Money actual = pr.AmountForItem(itemcode);

        Assert.Equal(expected, actual);
    }
}
  

Комментарии:

1. Не могли бы вы попробовать сделать ItemCode для реализации IEquatable<ItemCode> ?

Ответ №1:

При Dictionary обращении к его элементам он ищет равенство ключей.
Поскольку ItemCode это не примитивный тип, при сравнении new ItemCode("A") == new ItemCode("A") вы должны получить false в результате, потому что это разные экземпляры с одинаковым содержимым.
Что вы можете сделать, так это реализовать IEquatable<ItemCode> с помощью ItemCode класса:

 public class ItemCode : IEquatable<ItemCode>
{
    private readonly string itemCode;

    public ItemCode(string itemCode)
    {
        this.itemCode = itemCode;
    }

    public override int GetHashCode()
    {
        return itemCode != null ? itemCode.GetHashCode() : 0;
    }

    public bool Equals(ItemCode other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return string.Equals(itemCode, other.itemCode);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        return obj is ItemCode ic amp;amp; Equals(ic);
    }
}