Problem with Generic List Remove method

J

Joe Cool

I am having a strange problem with a generic list. Maybe someone can
spot my error. Specifically I am trying to remove an item when the
list is a list of class object instances.

public class myClass
{
public string myString { get; set; }
public int myInt { get; set; }

public myClass()
{
this.myString = string.Empty;
this.myInt = 0;
}
}

public class DoSomeWork
{
public List<myClass> myList = new List<myClass>();

public DoSomeWork()
{
this.myList = new List<myClass>();
}

public void SomeWork()
{
myClass myClassInstance = null;

myClassInstance = new myClass();
myClassInstance.myString = "ABCDEF";
myClassInstance.myInt = 10;
this.myList.Add(myClassInstance);
myClassInstance = new myClass();
myClassInstance.myString = "GHIJKL";
myClassInstance.myInt = 20;
this.myList.Add(myClassInstance);
myClassInstance = new myClass();
myClassInstance.myString = "MNOPQR";
myClassInstance.myInt = 35;
this.myList.Add(myClassInstance);
}

public void RemoveItem(string stringval, int intvalue)
{
myClass myClassInstance = null;

myClassInstance = new myClass(); /* breakpoint here */
myClassInstance.myString = stringval;
myClassInstance.myInt = intvalue;
this.myList.Remove(myClassInstance);
}
}

Later, in the application's main form this Button control Click event:

public partial class Form1 : Form
{
private DoSomeWork doSomeWork;

public Form1()
{
InitializeComponent();
this.doSomeWork = new DoSomeWork();
this.doSomeWork.SomeWork();
}

...

private void button1_Click(object sender, EventArgs e)
{
this.doSomeWork.RemoveItem("GHIJKL", 20);
}
}

I set a breakpoint as indicated in the RemoveItem method and run in
Debug mode. I click on the button
and when the breakpoint hits, I examine the Count property of the
myList object and the value is 3
as expected.

I then single step through the RemoveItem method call. I then re-
examine the Count property of the
myList object and it is still 3.

I would expect it to be 2.

Any thoughts?
 
J

Joe Cool

Joe said:
[...]
I set a breakpoint as indicated in the RemoveItem method and run in
Debug mode. I click on the button
and when the breakpoint hits, I examine the Count property of the
myList object and the value is 3
as expected.
I then single step through the RemoveItem method call. I then re-
examine the Count property of the
myList object and it is still 3.
I would expect it to be 2.
Any thoughts?

Yes. You are calling List<T>.Remove() with an instance of "myClass" that
isn't contained in the list, and the default comparison for reference
types in a List<T> is to simply compare the references.  The reference
of the new instance is of course different from the reference of the
instance that's actually in the list.  So there's nothing for it to remove.

Note that you could have checked the return value of the Remove() method
to see whether the method did in fact successfully remove something or
not.  It will return "false" when nothing is removed.  At least that
would have made clear that there's nothing mysterious going on; the code
simply isn't written correctly.

You have a few different options that would do what you actually appear
to be trying to do:

   • Make "myClass" a struct instead.  The default comparison for
structs is different than that for classes.  In particular, it will do a
field-by-field comparison, calling object.Equals() for each field,
rather than just testing for reference equality.

   • Implement IEquatable<myClass> in "myClass", such that instead of
doing a reference-equality comparison, you are actually comparing the
fields of your type.  Or…

   • …override Object.Equals() and Object.GetHashCode().  Overriding
Object.Equals() will have the same effect that implementing
IEquatable<myClass> will, and whenever you override Object.Equals() you
also must override Object.GetHashCode().

   • Search the list yourself by index, and then use the
List<T>.RemoveAt() method to remove the element you're looking for when
you find it.

No doubt there are other approaches that would also solve the problem,
but the above are the most obvious and simplest ones I can think of.

Pete

Thanks. I have never used a generic list of class object instances
where I actually saved the original instances that were added to the
list, so it seems to me that the Remove method is pretty useless, at
least for me.

I plan to use the last technique you mentioned. Actually, that is what
I usually use, I just thought the Remove method would be a cleaner way
to do it, but I misunderstood exactly how the Remove method works.
 
P

Peter Duniho

Joe said:
Thanks. I have never used a generic list of class object instances
where I actually saved the original instances that were added to the
list, so it seems to me that the Remove method is pretty useless, at
least for me. [...]

For anyone doing things beyond the most simplistic of data management,
it is not uncommon at all for objects to implement IEquatable<T> or
override Equals() and GetHashCode(), in which case List<T>.Remove()
works just as you'd expected it to.

There are also plenty of valid scenarios where you have a list of
objects and simply do want to remove a specific instance of an object
from the list that you already have referenced in a variable. Once
you've done a broader range of programming, you'll see this come up from
time to time.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top