Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom (derived) List<T>

Feel free to load your guns and take aim, but I want to understand why you shouldn't do this.

I have created a custom class designed to replace any instances of List (which I use to update XML objects behind them):

public class ListwAddRemove<T> : List<T> {
    public event EventHandler<ListModifyEventArgs> OnAdd;
    public event EventHandler<ListModifyEventArgs> OnRemove;

    new public void Add(T o) {
        base.Add(o);

        if (OnAdd != null) {
            OnAdd(this, new ListModifyEventArgs(o));
        }
    }

    new public void Remove(T o) {
        base.Remove(o);

        if (OnRemove != null) {
            OnRemove(this, new ListModifyEventArgs(o));
        }
    }
}

The idea is whenever I add or remove an item from this list my bound events will fire and I can deal with the XML behind automatically.

This works like a charm, so far so good.

But how do I handle a conversion between object.ToList() and my derived version?

A lot of people are saying you should derive from Collection instead... why?

like image 668
user1830285 Avatar asked Dec 10 '13 11:12

user1830285


2 Answers

You should derive from Collection<T> because it's designed to allow you to override InsertItem, and RemoveItem to add custom behavior such as what you're doing (also SetItem, to add custom behavior when changing an existing item).

It can therefore be used as an IList<T>, and any insertion/removal will automatically use the customisation.

In your case, anyone who casts to IList<T> or the base class List<T> will bypass your custom Add/Remove functionality.

Collection<T> also provides a constructor to wrap an existing list. You can expose this from your derived class to wrap a list generated by Enumerable<T>.ToList().

UPDATE

Whats the syntax to expose the constructor please?

Very simple:

public class ListwAddRemove<T> : Collection<T>
{
    public ListwAddRemove<T>()
    {
    }

    public ListwAddRemove<T>(IList<T> list) : base(list)
    {
    }

    ... implementation of overrides for InsertItem, SetItem, RemoveItem ...
}

Then use it as follows:

IList<SomeType> list = ....ToList();
ListwAddRemove<SomeType> myList = new ListwAddRemove<SomeType>(list);
like image 145
Joe Avatar answered Oct 19 '22 00:10

Joe


For one,

void DoSomeAddingToList(List<int> list) {
  list.Add(1);
}

var list = new ListwAddRemove<int>();
DoSomeAddingToList(list);

will not trigger the events. That might lead to strange effect, especially if you're not the only one using the class.

List<T> defines a very specific behaviour for Add and Remove (since it's a concrete class), and users might rely on exactly this behaviour.

I think this is generally true for using a new modifier, so this language feature should be used with caution, especially on public methods.

As others have mentioned, implementing IList<T> (using delegation/aggregation) is probably the better choice.

like image 22
MartinStettner Avatar answered Oct 18 '22 23:10

MartinStettner