views:

972

answers:

6

I have an ICollection<T> called foos in my class which I want to expose as read-only (see this question). I see that the interface defines a property .IsReadOnly, which seems appropriate... My question is this: how do I make it obvious to the consumer of the class that foos is read-only?

I don't want to rely on them remembering to query .IsReadOnly before trying a not-implemented method such as .Add(). Ideally I would like to expose foos as a ReadOnlyCollection<T>, but it does not implement IList<T>. Should I expose foo via a method called eg. GetReadOnlyFooCollection rather than via a property? If so, would this not confuse someone who then expects a ReadOnlyCollection<T>?

Edit: C# 2.0, so no extension methods like ToList() available...

+9  A: 

You can make "foos" a ReadOnlyCollection like this:

ReadOnlyCollection<T> readOnlyCollection = foos.ToList<T>().AsReadOnly();

Then you can expose it as a property of your class.

EDIT:

    class FooContainer
    {
        private ICollection<Foo> foos;
        public ReadOnlyCollection<Foo> ReadOnlyFoos { get { return foos.ToList<Foo>().AsReadOnly();} }

    }

Note: You should remember that once you get the ReadOnlyFoos collection is no longer "synchronized" with your foos ICollection. See the thread you referenced.

bruno conde
+2  A: 

My recomendation is to return use a ReadOnlyCollection\ for the scenario directly. This makes the usage explicit to the calling user.

Normally I would suggest using the appropriate interface. But given that the .Net Framework does not currently have a suitable IReadOnlyCollection, you must go with the ReadOnlyCollection type.

Also you must beware when using ReadOnlyCollection because it is not actually readonly: Immutability and ReadOnlyCollection

JaredPar
I would love to return a ReadOnlyCollection: are you suggesting implementing the remaining IList methods myself in a wrapper?
Joel in Gö
ReadOnlyCollection implements IList, even in .NET 2.0: http://msdn.microsoft.com/en-us/library/ms132474(VS.80).aspx
Don Kirkby
Exactly - my ICollection, however, doesn't, so I would need to implement the remaining methods in a new wrapper class if I wanted to pass it out as an IList. See the thread I referenced in the original question.
Joel in Gö
A: 

What about returning a T[]?

private ICollection<T> items;

public T[] Items
{
    get { return new List<T>(items).ToArray(); }
}
Juanma
for why this is a bad idea, see: http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat-harmful.aspx
Jacob Carpenter
I agree that this is a bad idea.
Jamie Penney
This isn't readonly, it's just copies of the objects in an array. Plus it's just a bad idea to return arrays of data.
Jon Tackabury
A: 

Sometimes you may want to use an interface, perhaps because you want to mock the collection during unit testing. Please see my blog entry for adding your own interface to ReadonlyCollection by using an adapter.

+1  A: 

I typically return an IEnumerable<T>.

Once you make a collection readonly (so methods like Add, Remove and Clear no longer work) there's not much left that a collection supports that an enumerable doesn't - just Count and Contains, I believe.

If consumers of your class really need to treat elements like they're in a collection, it's easy enough to pass an IEnumerable to List<T>'s constructor.

John Price
+2  A: 

I seem to have settled on returning IEnumerable with the objects cloned.

public IEnumerable<Foose> GetFooseList() {
   foreach(var foos in Collection) {
     yield return foos.Clone();
   }
}
  • requires a Clone method on Foos.

This allows no changes in the collection. Remember that ReadonlyCollection is "leaky" since the objects inside it can be changed as mentioned in a link in another post.

Mank