Dictionary using is custom key but key is always unequal

143 views Asked by At

I am using RTBTextPointer as custom key in dictionary...

   Init.SpintaxEditorPropertyMain.SpintaxListDict = new Dictionary<RTBTextPointer, SpintaxEditorProperties.SpintaxMappedValue>(new RTBTextPointerComparer());

I worte this RTBTextPointer, and RTBTextPointerComparer classes in class library and using this in different wpf projects,

 if (Init.SpintaxEditorPropertyMain.SpintaxListDict.ContainsKey(_index) == false)
        {
            Init.SpintaxEditorPropertyMain.SpintaxListDict.Add(_index,_SpintaxMappedVal);
        }

everytime containsKey returns false, even it contains, so duplication entry occurs in dictionary.. is anything wrong in my "GetHashCode()"

    public class RTBTextPointer
    {
    static int _row;
    static int _column;

    public int Row
    {
        get
        {
            return _row;
        }
        set
        {
            _row = value;
        }
    }
    public int Column
    {
        get
        {
            return _column;
        }
        set
        {
            _column = value;
        }
    }

}

public class RTBTextPointerComparer : IEqualityComparer<RTBTextPointer>
{
    public bool Equals(RTBTextPointer x, RTBTextPointer y)
    {         
        bool result = int.Equals(x.Column, y.Column) && (int.Equals(x.Row, y.Row));

        return result;
    }

    public int GetHashCode(RTBTextPointer obj)
    {
        var result = 0;
        int hCode = obj.Column ^ obj.Row;
        result = hCode.GetHashCode();
        return result;
    }
}

Please help me Thanks in advance

2

There are 2 answers

0
Corak On BEST ANSWER

I don't think you need to create a separate comparer. Just overriding Equals and GetHashCode should suffice.

Also, if you have very simple properties like that, you could switch to auto properties

public class RTBTextPointer
{
    public int Row
    {
        get;
        set;
    }
    public int Column
    {
        get;
        set;
    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }
        var other = obj as RTBTextPointer;
        if (other == null)
        {
            return false;
        }
        return other.Row == Row && other.Column == Column;
    }
    public override int GetHashCode()
    {
        unchecked
        {
            // 397 or some other prime number
            return (Row * 397) ^ Column;
        }
    }
}

See unchecked for more information about that.

If you have more than two properties, and if those properties could be null, the GetHashCode might look like this:

unchecked
{
    var result = 0;
    result = (result * 397) ^ (Prop1 != null ? Prop1.GetHashCode() : 0);
    result = (result * 397) ^ (Prop2 != null ? Prop2.GetHashCode() : 0);
    result = (result * 397) ^ (Prop3 != null ? Prop3.GetHashCode() : 0);
    result = (result * 397) ^ (Prop4 != null ? Prop4.GetHashCode() : 0);
    // ...
    return result;
}
1
jdphenix On

Your problem probably stems from the following declarations in RTBTextPointer:

static int _row;
static int _column;

These don't do what I think you're intending. They should be

private int _row; 
private int _column; 

As it is right now, these variables reference static members of RTBTextPointer. This means that any access of them will access or mutate the static members of it. static members are accessible to every instance of a type. If you make them private, they will apply per instance, which I believe is your intent.

Once that is corrected, I would reconsider the design of your class, at least if you intent to use it as a key in a Dictionary. RTBTextPointer should be immutable, or atleast the fields and properties that GetHashCode() depends on. Here's why:

When you add a object as a key to a dictionary, it's associated value is placed in a hash bucket , which is simply some data structure associated with a hash code. Assume we have some arbitrary key RTBTextPointer with Row = 2 and Column = 2 and a value of "Foo". It's GetHashCode would be 0 (2 XOR 2).

Hash Key                  Value
0    RTBTextPointer(2,2)  Foo

Right now, a call to Dictionary.ContainsKey() would return true looking for RTBTextPointer(2,2). Now consider if this RTBTextPointer changed to have a Row = 4. It's hash code would now be 6 (4 XOR 2). The call to Dictionary.ContainsKey() would now be false, and the value Foo would be inaccessible because the key has a hash code that depends upon mutable state.

As a final note, I would consider overriding the Equals() and GetHashCode() methods of object.