1

I have an list of FilterItems (called filter). Each FilterItem consists of a string & a boolean value. e.g. "0" and false.

I have declared the list like so:

 List<FilterItem> filter = new List<FilterItem>();

I'm then iterating through a datatable column, the intention is to add every single value from that column into filter - and if a value occurs twice - don't add a repeat entry to the filter List. This is what i'm doing:

List<FilterItem> filter = new List<FilterItem>(); //initialise new list
foreach (DataRow row in RawResults.Rows) //For each row in DT
{
    FilterItem myItem = new FilterItem { Name = row.ItemArray[i].ToString(), Checked = CheckState.Checked }; //create item
    if (!filter.Contains(myItem)) //if item doesn't exist
    {
        filter.Add(myItem); //add it
    }
}

The problem is that the result of Contains is always false even when myItem is identical to items already in the list - I think this is beacuse of the foreach loop - but I don't understand why because the filter List is declared outside of that loop. I've tried declaring myItem outside of the foreach loop too and that makes zero difference.

If I do the following:

List<FilterItem> filter = new List<FilterItem>(); //initialise new list
foreach (DataRow row in RawResults.Rows) //For each row in DT
{
    FilterItem myItem = new FilterItem { Name = row.ItemArray[i].ToString(), Checked = CheckState.Checked }; //create item
    if (!filter.Contains(myItem)) //if item doesn't exist
    {
        filter.Add(myItem); //add it
    }
    FilterItem myItem = new FilterItem { Name = row.ItemArray[i].ToString(), Checked = CheckState.Checked }; //create item
    if (!filter.Contains(myItem)) //if item doesn't exist
    {
        filter.Add(myItem); //add it
    }
}

The 2nd time .Contains is called (within the same iteration of the foreach loop) it correctly goes to true, just not when the foreach loop is iterated around again.

John Bergqvist
  • 852
  • 13
  • 38
  • 1
    Show the code for `FilterItem`. My guess is that it doesn't override `Equals` and `GetHashCode`, which means `Contains` will do reference equality instead of value equality. – juharr Sep 26 '16 at 11:40

4 Answers4

2

'The problem is that the result of Contained is always false even when myItem is identical to items already in the list ' In fact it's not - you are always instantiating new objects and .Contains method only compares the references. The best way to do in here is to check if there is already an item on the list with the same set of properties:

!filter.Any(item => item.Name == myItem.Namae && item.Checked == myItem.Checked);

Should do the trick, cause you are comparing not the references but actual properties of an item.

Łukasz Trzewik
  • 1,165
  • 2
  • 11
  • 26
0

According to this thread, .Contains() uses default IEqualityComparer for class, thus, for most cases it compares references to objects.

In order to check if the element is indeed contained, you need to either use .Any() or implement own equality comparer.

Community
  • 1
  • 1
Jakub Jankowski
  • 731
  • 2
  • 9
  • 20
0

The code new FilerItem does exactly that, it creates a new filter item. That new item is different from any other object, even if the properties are exactly the same, it is a different instance.

If you change your first if-statement it to:

if(!filter.Any(i => i.Name == myItem.Name && i.Checked == myItem.Checked)) { //...

You will actually compare the properties. Alternatively you could override the Equals method in the FilterItem class or implement IEquatable<T>.

GWigWam
  • 2,013
  • 4
  • 28
  • 34
0

You should consider overriding Equals and GetHashCode methods within FilterItem class. This way, depending on the collection used to store FilterItem instances, corresponding method will be triggered upon calling Add/Contains methods on that particular collection.

grale
  • 1
  • 1