Problem Overview
I see many developers struggle very often when trying to remove an item from a collection. I just came across a scenario where my teammate was having a nightmare with this issue. He had a customer observable collection. When the user selects a customer from a datagrid and trying to remove it from the collection using the collection.Remove() method, it just stays for no reason.
So, he then tried the old foreach loop for deletion and forgot that he can't remove any data while looping. He got the usual exception.
I will try to provide you a general C# solution for this. The problem is the basic understanding of the comparing object. Remove() internally uses an Equals method to check whether the object matches with each other.
Important to know about Equals() and GetHashCode()
The methods GetHashCode() and Equals() play a distinct role in the objects you play with for collections. When you create your own class it automatically derives from the base Object class. Your class should have more than one property. So when do you say the objects are equal? How will the framework determine it? What's your concept of equal? For example: say your class has two properties, namely ID and Name. Now for one developer in company A, two objects are equal when ID is equal. It might happen that in company B, the requirement is the objects are equal when both the properties are equal.
That's why the framework gave the responsibility to the developer to decide when the objects will call equal. For that when you create your model class, it's important that you override the equal method.
As much as is reasonably practical, the GetHashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the C# programming language.)
The default implementation of the Equals() method checks to see if the two objects have the same identity. Similarly, the default implementation of the GetHashCode() method returns an integer based on the object's identity and is not based on the values of instance (and class) variables of the object. No matter how many times the values of its instance variables (data fields) change, the hash code calculated by the default hashCode implementation does not change during the life of the object. That's why we always implement GetHashCode() when you are overriding the Equals() method.
Code
Our class:
class Person
{
public string Name { get; set; }
public int ID { get; set; }
public override string ToString()
{
return "Name:" + Name + " & ID: " + ID;
}
}
Now try to remove an item:
var collection = new ObservableCollection<Person>(){new Person{Name="Bubu",ID=1},new Person{Name="Arunava",RollNo=2}};
collection.Remove(new Person { Name = "Arunava", ID = 2 });
collection.ToList().ForEach(c =>Console.WriteLine(" " + c));
Console.ReadLine();
The output will still contain 2 items, in other words your Remove method fails.
Solution
Implement Equals() and GetHashCode() for the Person class.
class Person
{
public string Name { get; set; }
public int ID { get; set; }
public override string ToString()
{
return "Name:" + Name + " & ID: " + ID;
}
public override bool Equals(object obj)
{
// Check for null
if (ReferenceEquals(obj, null))
return false;
// Check for same reference
if (ReferenceEquals(this, obj))
return true;
var person = (Person)obj;
return this.ID == person.ID;
}
public override int GetHashCode()
{
return ID ^ 7;
}
}
Now try to remove an item as in the following:
var collection = new ObservableCollection<Person>(){new Person{Name="Bubu",ID=1},new Person{Name="Arunava",RollNo=2}};
collection.Remove(new Person { Name = "Arunava", ID = 2 });
collection.ToList().ForEach(c =>Console.WriteLine(" " + c));
Console.ReadLine();
You will get your desired output.
Please Note: There are better ways to implement Equals() and GetHashCode() for multiple scenarios. Please go through best practices before implementing those. Thanks. I hope this helps.