Search code examples
c#dictionarywrapperequalsgethashcode

Instances of my class don't want to act properly as dictionary keys interchangeably with strings


I created a sort of string wrapper class and want to use its instances as dictionary keys interchangeably with usual strings. I overrode GetHashCode and Equals and got results that seem strange. I've isolated the issue. Please look at my code and explain me why does second lookup returns null.

void Main()
{
    var foo = new StringWrapper("foo");
    var h = new Hashtable {{ foo, "bar" }};
    Console.WriteLine(h["foo"]);
    Console.WriteLine(h[foo]); // null ??
}

public class StringWrapper
{
    readonly string wrapped;
    public StringWrapper(string s) {
        wrapped = s;
    }
    public override bool Equals(object obj) {
        return wrapped.Equals(obj);
    }
    public override int GetHashCode() {
        return wrapped.GetHashCode();
    }
    public override string ToString() {
        return wrapped;
    }
}

Solution

  • Your equality implementation isn't symmetric, nor reflexive:

    StringWrapper wrapper = new StringWrapper("foo");
    
    Console.WriteLine(wrapper.Equals(wrapper)); // False
    Console.WriteLine(wrapper.Equals("foo")); // True
    Console.WriteLine("foo".Equals(wrapper)); // False
    

    So you're violating the rules specified in the docs for Object.Equals. Hashtable (and other classes) expect you not to violate those rules, and won't work properly when you violate them.

    It sounds like you need a custom collection, rather than a wrapper which tries to pretend that the wrapper is equal to the original value.

    (As an aside, why are you still using non-generic Hashtable rather than Dictionary<,>?)