ImmutableHashSet.Contains returns false

I have a list (to be precise ImmutableHashSet<ListItem> of System.Collections.Immutable) of the base elements and try calling the following code

 _baseList.Contains(derivedItem) 

but it returns false .

Even if the following lines of code return true

 object.ReferenceEquals(_baseList.First(), derivedItem) object.Equals(_baseList.First(), derivedItem) _baseList.First().GetHashCode() == derivedItem.GetHashCode() 

I can even write the following and it returns true:

 _baseList.OfType<DerivedClass>().Contains(derivedItem) 

What I am doing wrong, I would like not to write .OfType stuff.

Edit:

 private ImmutableHashSet<BaseClass> _baseList; public class BaseClass { } public class DerivedClass : BaseClass { } public void DoStuff() { var items = _baseList.OfType<DerivedClass>().ToList(); foreach (var derivedItem in items) { RemoveItem(derivedItem); } } public void RemoveItem(BaseClass derivedItem) { if (_baseList.Contains(derivedItem)) { //doesn't reach this place, since _baseList.Contains(derivedItem) returns false... _baseList = _baseList.Remove(derivedItem); } //object.ReferenceEquals(_baseList.First(), derivedItem) == true //object.Equals(_baseList.First(), derivedItem) == true //_baseList.First().GetHashCode() == derivedItem.GetHashCode() == true //_baseList.OfType<DerivedClass>().Contains(derivedItem) == true } 

Edit2:

Here, the reproduced code of my problem is similar to ImmutableHashSet<> caches GetHashCode and does not compare the current GetHashCode with the entries inside the list, is there a way to tell ImmutableHashSet<> that the GetHashCode elements can be different, at least for the element I'm checking now, so how is he his damn link ...

 namespace ConsoleApplication1 { class Program { private static ImmutableHashSet<BaseClass> _baseList; static void Main(string[] args) { _baseList = ImmutableHashSet.Create<BaseClass>(); _baseList = _baseList.Add(new DerivedClass("B1")); _baseList = _baseList.Add(new DerivedClass("B2")); _baseList = _baseList.Add(new DerivedClass("B3")); _baseList = _baseList.Add(new DerivedClass("B4")); _baseList = _baseList.Add(new DerivedClass("B5")); DoStuff(); Console.WriteLine(_baseList.Count); //output is 5 - put it should be 0... Console.ReadLine(); } private static void DoStuff() { var items = _baseList.OfType<DerivedClass>().ToList(); foreach (var derivedItem in items) { derivedItem.BaseString += "Change..."; RemoveItem(derivedItem); } } private static void RemoveItem(BaseClass derivedItem) { if (_baseList.Contains(derivedItem)) { _baseList = _baseList.Remove(derivedItem); } } } public abstract class BaseClass { private string _baseString; public string BaseString { get { return _baseString; } set { _baseString = value; } } public BaseClass(string baseString) { _baseString = baseString; } public override int GetHashCode() { unchecked { int hashCode = (_baseString != null ? _baseString.GetHashCode() : 0); return hashCode; } } } public class DerivedClass : BaseClass { public DerivedClass(string baseString) : base(baseString) { } } } 

If I changed the value of ImmutableHashSet<> to ImmutableList<> , the code would work just fine, so if you guys haven’t come up with any good idea, I will switch to the list.

+6
source share
2 answers

Objects used in dictionaries and other hash-related data structures must be permanently identified. All hash-related data structures assume that after adding an object to the dictionary, its hash code will not change.

This code will not work:

  private static void DoStuff() { var items = _baseList.OfType<DerivedClass>().ToList(); foreach (var derivedItem in items) { derivedItem.BaseString += "Change..."; RemoveItem(derivedItem); } } private static void RemoveItem(BaseClass derivedItem) { if (_baseList.Contains(derivedItem)) { _baseList = _baseList.Remove(derivedItem); } } 

_baseList.Contains() in RemoveItem() , called by DoStuff() , will return false for each individual element, because you changed the identifier of the saved element - its BaseString property.

+4
source

I think you answered your question in your editing. You cannot change hashCode after adding an item to a HashSet. This violates the contract on how the HashSet works.

See this great article by Eric Lippert for more information on this topic.

In particular, he says the following:

Guideline: the integer returned by GetHashCode should never change

Ideally, the hash code of a mutable object should be calculated only from fields that cannot mutate, so the hash value of the object is the same for its entire life cycle.

However, this is only an ideal situation; actual rule:

Rule: the integer returned by GetHashCode should never change while the object is contained in the data structure, which depends on the remaining hash code

It is acceptable, albeit dangerous, to make an object whose hash code value can mutate, because the fields of the object mutate. If you have such an object and you put it in a hash table, then the code that mutates the object and the code that supports the hash table should have some consistent protocol that ensures that the object is not mutated while it is in the hash -table. What this protocol looks like is up to you.

If the hash code of an object can mutate when it is in the hash table, then obviously the Contains method stops working . You put an object in bucket # 5, you mutate it, and when you ask if it contains a mutated object, it looks in bucket # 74 and does not find it.

Remember that objects can be placed in hash tables in ways you did not expect. Many LINQ sequence operators use internal hash tables. Do not send dangerous mutant objects by listing a LINQ query that returns them!

EDIT : BTW, your post and subsequent editing is a great example of why you should always publish the full and reproducible working code of your problem from the very beginning, rather than trying to filter out what you feel is irrelevant information. Almost everyone who looks at your message an hour ago could give you the correct answer within a second of a second if they had all the relevant information to begin with.

+3
source

Source: https://habr.com/ru/post/988513/


All Articles