views:

71

answers:

4

I have a class named WhatClass that has List field in it. I need to be able to read-only this field, so I used a get property to expose it to other objects.

public class WhatClass
{
    List<SomeOtherClass> _SomeOtherClassItems;

    public List<SomeOtherClass> SomeOtherClassItems { get { return _SomeOtherClassItems; } }
}

However it turns out that any object can call

WhatClass.SomeOtherClassItems.Add(item);

How can I prevent this?

+1  A: 

How about using AsReadOnly()? - MSDN Documentation

Marcel J.
+1 for being the first to answer.
Robert Harvey
+4  A: 

Use List<T>.AsReadOnly:

public ReadOnlyCollection<SomeOtherClass> SomeOtherClassItems
{
    get
    {
        return _SomeOtherClassItems.AsReadOnly();
    }
}

This will return a ReadOnlyCollection, which will throw an exception if a client calls Add through the interface. In addition, the ReadOnlyCollection type does not expose a public Add method.

Quartermeister
Don't make a new instance each time.
SLaks
@SLaks: I see your point, but that's a micro-optimization and I believe it's easier to read this way. ReadOnlyCollection is small and the GC is good at dealing with small short-lived objects, and you're just as likely to wind up using more memory by adding a field and a ReadOnlyCollection object that you don't need for every one of your classes.
Quartermeister
@Quartermeister: Allocating objects in a property getter is a bad idea. People generally assume that a accessing a property getter is nothing more than a memory read, and will therefore access the property many times in large nested loops.
SLaks
@SLaks: Never mind, I see the argument about reference equality in Bryan's answer. I hadn't considered that.
Quartermeister
+2  A: 

You're looking for the ReadOnlyCollection<T> class, which is a read-only wrapper around an IList<T>.

Since the ReadOnlyCollection<T> will reflect changes in the underlying list, you don't need to create a new instance every time.

For example:

public class WhatClass {
    public WhatClass() {
        _SomeOtherClassItems = new List<SomeOtherClass>();
        SomeOtherClassItems = _SomeOtherClassItems.AsReadOnly();
    }

    List<SomeOtherClass> _SomeOtherClassItems;

    public ReadOnlyCollection<SomeOtherClass> SomeOtherClassItems { get; private set; }
}
SLaks
Thank you for your answer. You mentioned ReadOnlyCollection<T> class, but didn't include it in the code, but I've figured it out. Thank you once again.
Witchunter
+6  A: 

As others have said, you are looking for the .AsReadOnly() extension method.

However, you should store a reference to the collection instead of creating it during each property access:

private readonly List<SomeOtherClass> _items;

public WhatClass()
{
    _items = new List<SomeOtherClass>();

    this.Items = _items.AsReadOnly();
}

public ReadOnlyCollection<SomeOtherClass> Items { get; private set; }

This is to ensure that x.Items == x.Items holds true, which could otherwise be very unexpected for API consumers.

Exposing ReadOnlyCollection<> communicates your intent of a read-only collection to consumers. Changes to _items will be reflected in Items.

Bryan Watts
You might want to elaborate for the masses what happens when you add to _items and how you deal with that...
Dave Markle
@Dave - The docs state that changes will be reflected in the wrapper.
ChaosPandion
Bryan, thank you very much for this answer, although I do not need a readonly keyword here.
Witchunter
@Witchunter: you aren't assigning `_items` after the constructor; the `readonly` keyword communicates that intent: http://msdn.microsoft.com/en-us/library/acdd6hb7.aspx. If you plan on assigning another value to `_items` after `WhatClass` is constructed, you will also need to reassign the `Items` property.
Bryan Watts