-2

I have a strange error in my code...

Code:

public class PropertyCollection<T> : IDictionary<T, string>
{
        private Dictionary<T, string> dict;
        ...
        public string this[T key]
        {
            get
            {
                bool has_key = this.Keys.Any(x => x == key); 
                return this.dict[key];
            }
            set
            {
                this.dict[key] = value;
            }
        }
        ...
}

The first row

bool has_key = this.Keys.Any(x => x == key);

returns true.

But

return this.dict[key];

throws the error:

System.Collections.ListDictionaryInternal.NodeKeyValueCollection: The given key was not present in the dictionary.

How this could be?

If I change the row, that throws an exception, to

return this.dict[this.Keys.First(x => x == key)];  

everything becomes normal and there is now error.

newfurniturey
  • 37,556
  • 9
  • 94
  • 102
BigMan
  • 425
  • 5
  • 13
  • 3
    just use dict.containskey – Alex Krupka Oct 15 '15 at 14:14
  • 6
    Doing this: `bool has_key = this.Keys.Any(x => x == key);` defeats the purpose of using a dictionary. – Yuval Itzchakov Oct 15 '15 at 14:14
  • 3
    If you want us to help, post a small, complete, compiling reproduce of your problem. – Yuval Itzchakov Oct 15 '15 at 14:15
  • 3
    What is `T` in this case? Do you override `Equals` on `T`? Did you also override `==`? Does the `Dictionary` use the default `EqualityComparer` or a different one? There are lots of reasons this could be the case. Some code demonstrating the issue would be useful. – Charles Mager Oct 15 '15 at 14:16
  • 4
    `T` probably has a broken `GetHashCode()` – SLaks Oct 15 '15 at 14:17
  • 2
    What is `this.Keys`? You appear to be confusing `this.Keys` with `this.dict.Keys`. They aren't necessarily the same thing. – Alex Walker Oct 15 '15 at 14:17
  • Well, in your code you don't use Dictionary collection in a proper way. So google for how to use it and read some information about this matter. If you'll still have some problem - create short but complete example of your problem and post it to SO – Fabjan Oct 15 '15 at 14:20
  • Also, why is `x == key` allowed when the right-hand side `key` has type `T` which is a type parameter without any contraints? Are you hiding some constraint from us? – Jeppe Stig Nielsen Oct 15 '15 at 14:22
  • First few comments are strange) bool has_key = this.Keys.Any(x => x == key); - are givin just to look what is going on, of cause. – BigMan Oct 15 '15 at 14:22
  • 1
    @BigMan First few comments must have been posted before the posters have finished reading your question. – Sergey Kalinichenko Oct 15 '15 at 14:23
  • `T` has override the `==` and `EqualityComparer` – BigMan Oct 15 '15 at 14:24
  • What is the result of `this.dict.Keys.Any(x => x == key)`? – DavidG Oct 15 '15 at 14:26
  • @BigMan well show some code that tells us what you are overriding and how you are doing it. It's likely this that causes the difference. – Charles Mager Oct 15 '15 at 14:27

1 Answers1

2

Assuming that T overrides both Equals and GetHashCode*, the only way I could thing how this would happen is if your key is mutable, its GetHashCode method uses mutable fields, and you called a mutating method after the key has been inserted into the dictionary.

This would make the linear search this.Keys.Any(x => x == key) produce true, but hash-based search would produce an exception.

Confirming this is very easy: the code below should print false:

var first = dict.Keys.First(x => x == key);
Console.WriteLine(first.GetHashCode() == key.GetHashCode());

* If T does not override one or both these methods, see this Q&A.

Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    Another possibility is that `==` and `Equals` have different semantics for the relevant type `T`. – Jeppe Stig Nielsen Oct 15 '15 at 14:23
  • Or the classical one: `T` overrides `Equals`, but it does not override `GetHashCode`, or that override calls `base.GetHashCode` or is incorrect in some other way. – Jeppe Stig Nielsen Oct 15 '15 at 14:31
  • @JeppeStigNielsen Yes, that's very likely too. I edited to mention a related Q&A. – Sergey Kalinichenko Oct 15 '15 at 14:37
  • 3
    Yes, you are right. `T` overrides both `Equals` and `GetHashCode` and even `==` and `!=`, but `GetHashCode` was `return base.GetHashCode() ` My mistake. I am ashamed for this stupid error( – BigMan Oct 15 '15 at 14:51
  • 1
    This code overrides `GetHashCode()` but fails from mutability: `using System.Collections.Generic; using System.Linq; sealed class MuatbleInt { public int Val { get; set; } public override bool Equals(object obj) { var other = obj as MuatbleInt; return (object)other != (object)null && other.Val == Val; } public override int GetHashCode() { return Val; } internal static void Main() { var key = new MuatbleInt { Val = 42, }; var dict = new Dictionary { { key, "" }, }; key.Val = 666; var hasKey = dict.Keys.Any(key.Equals); var bang = dict[key]; } }` – Jeppe Stig Nielsen Oct 15 '15 at 15:00