views:

527

answers:

6

I have a class that contains a list of objects. What's the best way to run some code in the class when the list is modified?

class MyManagerClass
{
    ArrayList list = new ArrayList(); // will likely be a different collection class
    private OnItemAddedToList(object o)
    {
        // how to call this?
    }

    private OnItemRemovedFromList(object o)
    {
        // how to call this?  
    }
}
+1  A: 

Instead of using the ArrayList from System.Collections you should consider using the ArrayList from the C5 Generic Collection Library. It has events like CollectionChanged, ItemAdded etc.

mookid8000
+2  A: 

The method I've been using so far is to make a private collection class derived from System.Collections.ObjectModel.Collection<T>, and call the methods directly in the overrides:

class MyManagerClass
{
    private class MyCollection : System.Collections.ObjectModel.Collection<object>
    {
        private MyManagerClass manager;

        private MyCollection(MyManagerClass manager)
        {
            this.manager = manager;
        }

        protected override void InsertItem(int index, object item)
        {
            base.InsertItem(index, item);
            manager.OnItemAddedToList(item);
        }

        protected override void SetItem(int index, object item)
        {
            object oldItem = (index < base.Count) ? base[index] : null;
            base.SetItem(index, item);

            if (oldItem != null) {
                manager.OnItemRemovedFromList(oldItem);
            }

            manager.OnItemAddedToList(item);
        }

        protected override void RemoveItem(int index, object item)
        {
            base.RemoveItem(index, item);
            manager.OnItemRemovedFromList(item);
        }
    }

    private OnItemAddedToList(object o)    
    {
    }

    private OnItemRemovedFromList(object o)    
    {
    }
}

I'm not sure if this is the right way to do it though and I'd like to see if there are any better alternatives.

Ilia Jerebtsov
A: 

Personally I'd implement IList<T>, in a class that has Added, Removed and Replaced events/delegates. The class would have a private instance member of type List<T> that all implemented calls would delegate to, as well as raising the events.

You could additionally have OnBefore..., OnAfter if you prefer, allowing you to "cancel" the addition/removal of elements.

This is typically how it's done, but like mookid said, there are probably existing libraries out there that do a similar thing. The DataTable class implements a similar concept with the DataRow class, including addition, removal and updating.

You could combine the concepts so that items added to the collection can raise OnChanged events that "bubble" up to the collection and can be used to re-raised a ItemChanged event on the collection.

Neil Barnwell
Bubbling events is exactly the reason I'm looking for this functionality. Are collections with events good practice? The idea seems uncomfortable, because it sounds like something that MS would have already implemented, if it wasn't bad in some way.
Ilia Jerebtsov
+3  A: 

You could also consider using the System.Collections.ObjectModel.ObservableCollection<T> class, which provides this for you in the form of the CollectionChanged event, which gives you the info about what's changed.

Davermouse
+5  A: 

If you are writing custom collections anyway you can implement the INotifyCollectionChanged or the INotifyPropertyChanged interfaces. They add events that you can subscribe to that notifies the listener when the collections data is changed somehow.
Doing this you can subscribe to the events in code outside your collection.

Alternatively you can use the ObservableCollection<T> class which implements both these interfaces. Both the INotifyCollectionChanged interface the the ObservableCollection<T> are only available in .Net Framework 3.0 and above.

On .Net Framework 2.0 you can use the IBindingList interface to get the same functionality, but it is more complex to implement than the INotifyCollectionChanged interface in 3.0

Rune Grimstad
INotifyCollectionChanged seems to be 3.0 only as well. Is there anything similar in 2.0?Are collections with events good practice at all? It sounds like something that MS would have implemented from the start, if it wasn't bad in some way.
Ilia Jerebtsov
Hmm. You are right. I am 99% sure that there is a similar interface available in 2.0. I'll check it out.
Rune Grimstad
I found it :-) The IBindingList interface can be used this way.
Rune Grimstad
Oh. I don't see why adding events to a list should be bad. You only add subscribers where you need them so in all other cases the events just lie there doing nothing.
Rune Grimstad
IBindingList seems a bit overkill, what with all the sorting and searching by PropertyDescriptor... It's got the right event, but at this point implementing it is too much of a bother just to expose the feature generically. I suppose that's why they split it up in 3.0.
Ilia Jerebtsov
Sure, but then why aren't there any any collections with events already in .NET (up to 3.0)? It seems to be a fairly common and pretty useful functionality for it to be just ignored for no reason.
Ilia Jerebtsov
I agree. It is just one more feature that should have been in .NET earlier. They probably didn't see the need for it before now. Oh. If you are only using the list for your own use you can just define an interface that mimics the INotifyCollectionChanged interface yourself.
Rune Grimstad
It was in 2.0 - see BindingList<T>
Marc Gravell
+2  A: 

In 2.0 and above, there is BindingList<T> which has all the necessary hooks for an observable collection. There is also (as already mentioned) now an ObservableCollection<T> class, which is very similar.

Deriving a new collection from Collection<T> is also pretty reasonable in many circumstances. But don't derive from List<T>, as this has no (useful) virtual methods.

Marc Gravell
Ah. Of course! I knew there was one in 2.0 as well :-)
Rune Grimstad