views:

385

answers:

7

I have a class that maintains list of objects of another class. List of objects is a public property. I would like to prevent users from adding and removing objects directly to list like this:

      MyObject.MyListProperty.Add(object);

Instead I want them to use method that will internally do some processing and then add object to list.

I have some ideas:

  • create descendant of List<T> and override add and remove
  • return fresh copy of list through property getter (list is relatively short, not more than 30 objects)

Is there some collection interface that does not have Add and Remove?

Edit:
I'm going to go with ReadOnlyCollection<T>. Reason is that wrapped collection can be updated and changes will be immediately visible in read only object (see MSDN code examples for ReadOnlyCollection<T> and AsReadOnly()). This allows that read only list be created only once.

The problem with IEnumerable is that object can be casted back to original List<T> and then directly manipulated.

A: 

You could just give them an object which always returns an enumerator that they can use to access the structure?

Joe
+21  A: 

You can use ReadOnlyCollection - wrap your collection in this and return the ReadOnlyCollection to users of your class:

return new ReadOnlyCollection(innerCollection);

Or using the AsReadOnly method of the List<T> class:

return innerCollection.AsReadOnly();

The IEnumerable interface will do what you need, as it only has one member GetEnumerator(), that will only let you iterate over items.

Oded
AsReadOnly (http://msdn.microsoft.com/en-us/library/e78dcd75.aspx) isn't Linq
Rob Fonseca-Ensor
Thanks for the correction - fixed!
Oded
+3  A: 

Maybe AsReadOnly method will do the trick, to hand a read-only instance to public.

Otherwise, IEnumerable<T> has no Add nor Remove, so you can do something like:

private List<int> _myList;
public IEnumerable<int> MyList
{
   get
   {
      return this._myList.ToList();
   }
}

I would rather go for the IEnumerable<T> (+ copy) as for ReadOnlyCollection<T>, because: changes to your base list (which your read-only collection is created from) will immediately show up in your instance of the read-only collection. this may cause locking and straaange problems :)

Andreas Niedermair
why `return this._myList.ToList();` and not `return this._myList;`
Rob Fonseca-Ensor
What about `IList<int>` and `ToArray`? Best of both worlds, no?
Dan Tao
IList has an Add method, so the list wouldn't be readonly, and you can change the contents of an array (not that that would affect this class since the array is a copy). The only interface that solves the OP's problem is IEnumerable
Rob Fonseca-Ensor
i'm not that fan of arrays... dunno why ... if you hack my example a bit more, you can have a true iterator-pass-through implementation , which is much more better than creating another object (array) to iterate through elements!
Andreas Niedermair
@Rob Fonseca-Ensor: simple: hand out a copy, not the same ref. the caller my cast it to a list and can modified your internal datastore!
Andreas Niedermair
A: 

Doesn't really answer your question, but is useful information if you decide to implement one of your two ideas: You can use ToArray to create a copy and return an array.

Or you can do something like this: http://en.csharp-online.net/CSharp_Generics_Recipes%E2%80%94Making_Read-Only_Collections_the_Generic_Way

AaronLS
+5  A: 

Your easiest option would be to expose your list as one of the following IEnumerable, ICollection ReadOnlyCollection via a public property.

So you could create your own type of list which exposes your Items as one of the above but has an internal method for the adding e.g.

public class MyList<MyType>
{
    private List<MyType> items;

    public MyList()
    {
        items = new List<MyType>();
    }

    public IEnumerable Items { get { return items.AsEnumerable(); } }
    public Add(MyType item)
    {
        // do internal processing
        items.Add(item);
    }
}
James
Cool +1 for the explanation
Ravisha
Note that this doesn't prevent casting the collection back to List<MyType> and calling Add on it. ReadOnlyCollection is the way to go if you really need to prevent Adds. Also, there's no need to call items.AsEnumerable and I don't think this will compile as written because AsEnumerable() will return IEnumerable<MyType> and Items is typed as IEnumerable.
Jamie Ide
@Jamie `IEnumerable<MyType>` inherits from `IEnumerable`, so it will compile, but I agree that they should return IEnumerable<MyType>, and that AsEnumerable is redundant.
Rob Fonseca-Ensor
@Jamie, it will compile, however, I do agree the cast is possible. This was just an example to show what the user can do as I have mentioned previously in the post that ReadOnlyCollection *is* an option. Cheers.
James
+2  A: 

Out of the box, only IEnumerable<T> will provide this without exposing any Add and Remove public methods that might trick the client into getting a runtime exception.

If you want your public API to explicitely state that the collection is read-only and you need an indexer, you will have to write your own wrapper around existing List<T> or T[].

Florian Doyon
A: 

ICollection (the non-generic version) only got IEnumerable + Count

adrianm