Alex Zhukovskiy
Alex Zhukovskiy

Reputation: 10015

KeyNotFoundException when it exists

I'm getting the first key in a dictionary and comparing it with a lookup value. They are the same, but when I try to access the key from the dictionary I get a KeyNotFoundException:

    public void Draw(Graphics graphics, Shape shape)
    {
        foreach (var line in shape.Lines)
        {
            var tuple = shape.LinesToNurmalDictionary.Keys.First();

            bool equals = tuple == line;
            bool referenceEquals = ReferenceEquals(tuple, line);

            var invisible = shape.LinesToNurmalDictionary[line].All(x => x.Z < 0);

How can I fix it?

picture

Added: Dictionary is

Dictionary<Tuple<ShapePoint, ShapePoint>, List<ShapePoint>> LinesToNurmalDictionary;

where ShapePoint is a class

So i 'solved' my problem by using my own class instead of Tuple, but actualy question still unresponsed:

public class Line
{
    public ShapePoint A { get; set; }
    public ShapePoint B { get; set; }

    public List<ShapePoint> Normals { get; set; }

    public Line(ShapePoint a, ShapePoint b)
    {
        A = a;
        B = b;
        Normals = new List<ShapePoint>();
    }

    private sealed class DefaultEqualityComparer : IEqualityComparer<Line>
    {
        public bool Equals(Line x, Line y)
        {
            if (ReferenceEquals(x, y))
                return true;
            if (ReferenceEquals(x, null))
                return false;
            if (ReferenceEquals(y, null))
                return false;
            if (x.GetType() != y.GetType())
                return false;
            return Equals(x.A, y.A) && Equals(x.B, y.B);
        }

        public int GetHashCode(Line obj)
        {
            unchecked
            {
                return ((obj.A != null ? obj.A.GetHashCode() : 0)*397) ^ (obj.B != null ? obj.B.GetHashCode() : 0);
            }
        }
    }

    private static readonly IEqualityComparer<Line> DefaultComparerInstance = new DefaultEqualityComparer();

    public static IEqualityComparer<Line> DefaultComparer
    {
        get
        {
            return DefaultComparerInstance;
        }
    }
}

Upvotes: 1

Views: 992

Answers (1)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 61952

As tia already said in a comment, this happens because an entry is first added to the Dictionary<,> where the hash code of the key (here the Tuple<ShapePoint, ShapePoint>) has some value which is "saved" and kept by the Dictionary<,>. After that the key object is "mutated" (modified) in a way that changes its hash code. This leads to the Dictionary<,> being in a corrupt state.

Therefore when the key is searched for afterwards, it is not found. Its "new" hash code differs from the hash the Dictionary<,> thinks this key has.

Do not modify objects that might be keys in a Dictionary<,> (or members of a HashSet<> and so on) in a way that changes their hash codes.


Here's an example illustrating. The type:

class Changeable
{
    public int Prop { get; set; }

    public override int GetHashCode()
    {
        return Prop;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Changeable);
    }
    public bool Equals(Changeable other)
    {
        if (other == null)
            return false;

        return GetType() == other.GetType() && Prop == other.Prop;
    }
}

The code that gives the same problem:

var onlyInstance = new Changeable();

var dict = new Dictionary<Changeable, string>();

onlyInstance.Prop = 1;
dict.Add(onlyInstance, "what-ever");

onlyInstance.Prop = 2;
string restoredValue = dict[onlyInstance]; // throws!

Upvotes: 2

Related Questions