views:

226

answers:

3

How do you manage this simple scenario when you have an object with a list of items. EG:

public class ContainerObject
{
    IList<ChildObject> Children { get; }

    public void AddCustom(ChildObject toAdd)
    {
        // Some validation ...
        Children.Add(toAdd);
    }
}

Assuming the collection is initialised to an implementation of IList, is there any way in which to control the way in which items are added to the list?

For example, I have another method on the ContainerObject class that takes a ChildObject and adds it to the list. This method is required to perform some basic validation on the ChildObject before adding it.

I am being lazy in that I don't want to mess around and write a custom list interface (without an add method) that consuming developers need to implement. I am also using the ToList() method on the IQueryable interface so this is another reason for sticking with IList - it just works.

So, is there an approach in which you can police how items are added to the IList instance i.e. prevent use of the Add method and only allow adding to the collection via my custom method, or am I just asking for the impossible? ... and being lazy :(

I can think of a few hacky ways in which to check when items are added via my custom method or directly on the list but these seem hacky!

Anyone experience anything similar to this? If so, what did you do?

+5  A: 

You can make your property return a wrapper around the original list using ReadOnlyCollection<T>. This will ensure that callers don't add any items themselves. You can keep a reference to the original mutable list. Note that because the read-only collection is only a wrapper, callers which cache the read-only collection will still see additions that you make to the original list. That may or may not be a good thing depending on your expected use.

EDIT: To explain my caching comment...

Suppose a client did:

IList<ChildObject> originalChildren = container.Children;    
container.AddChild(new ChildObject());
IList<ChildObject> updatedChildren = container.Children;

with the Children property implemented like this:

private IList<ChildObject> children = new List<ChildObject>();
public IList<ChildObject> Children
{
    get { return new ReadOnlyCollection<ChildObject>(children); }
}

Then originalChildren and updateChildren would both have the same contents - the returned ReadOnlyCollection wouldn't be a snapshot of the collection of children at the first line. It would just be a wrapper around the collection. Clients wouldn't be able to rely on it not changing - they just wouldn't be able to change it themselves.

Jon Skeet
Sorry Jon, could you explain the caching comment in more detail please? Thanks
Tim Peel
Thanks Jon, appreciated
Tim Peel
A: 

Make your property return _list.ToArray() instead of the list itself.

ssg
I'm quite sure that would be expensive - ToArray hast to shallow-clone the list.
Henk Holterman
It's not as expensive as it sounds since List itself keep things in an array already. So it's just an Array.Copy() operation.
ssg
+2  A: 

Return a ReadOnlyCollection to the world wrapping your list which you manage the Adds to.

Rob McCready