HashSet <T> .RemoveWhere () and GetHashCode ()

Aloha

Here is a simple class that overrides GetHashCode:

class OverridesGetHashCode
{
    public string Text { get; set; }

    public override int GetHashCode()
    {
        return (Text != null ? Text.GetHashCode() : 0);
    }
    // overriding Equals() doesn't change anything, so I'll leave it out for brevity
}

When I instantiate this class, add it to the HashSet and then change its Text property, for example:

var hashset = new HashSet<OverridesGetHashCode>();
var oghc = new OverridesGetHashCode { Text = "1" };
hashset.Add(oghc);
oghc.Text = "2";

then this will not work:

var removedCount = hashset.RemoveWhere(c => ReferenceEquals(c, oghc));
// fails, nothing is removed
Assert.IsTrue(removedCount == 1);

and does not do this:

// this line works, i.e. it does find a single item matching the predicate
var existing = hashset.Single(c => ReferenceEquals(c, oghc));
// but this fails; nothing is removed again
var removed = hashset.Remove(existing);
Assert.IsTrue(removed); 

I assume that the hash that it uses internally is generated when the element is inserted, and if true, it is clear that hashset.Contains (oghc) is not working. I also assume that it looks at the element by its hash code, and if it finds a match, only then it checks the predicate, and that is probably why the first test fails (again, I just guess here). But why does the last test fail, I just got this object from hashset? Am I missing something, is this the wrong way to remove something from a HashSet?

, , .

. , Equals():

protected bool Equals(OverridesGetHashCode other)
    {
        return string.Equals(Text, other.Text);
    }

public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((OverridesGetHashCode) obj);
    }
+5
3

. HashSet<T>, , Add(value) :

Remove(value), 1 2., , . IEqualityComparer<T>.Equals(), , . , , GetHashCode(), /, . , .

, , Equals() , /, -.

+2

- , HashSet, HashSet.

, . - .

MSDN:

GetHashCode -, , Equals . , , , .

-, . , - . , , -. , , .

+4

, , (HashSet, Dictionary ..), ( , ).

To find an object in the data structure, it computes its hash code, and then finds a location based on that hash code. If you mutate this object, then the hash code that it returns no longer reflects its current location in this data structure (if you are not very, very lucky, and this is just a hash collision).

The MSDN dictionary page says:

As long as the object is used as a key in Dictionary<TKey, TValue>, it should not in any way change its hash value.

The same statement applies HashSet, since both of them are implemented using hash tables.

+4
source