Nikola Malešević
Nikola Malešević

Reputation: 1858

How to make List's Add method protected, while exposing List with get property?

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?

Upvotes: 31

Views: 15701

Answers (7)

tia
tia

Reputation: 9698

List<T> implements IReadOnlyList<T> since .NET Framework 4.5, so an internal list can be exposed as IReadOnlyList<T> instead for that purpose.

public class WhatClass
{
    List<SomeOtherClass> _SomeOtherClassItems;

    public IReadOnlyList<SomeOtherClass> SomeOtherClassItems => _SomeOtherClassItems;
}

Upvotes: 2

Amir Mahdi Nassiri
Amir Mahdi Nassiri

Reputation: 1330

Although others have pointed out the use of ReadOnlyCollection, it's worth noting that there also exists a cool ReadOnlyObservableCollection. It can be really useful in bindings and view models. As pointed here:

This class is a read-only wrapper around an ObservableCollection. If changes are made to the underlying collection, the ReadOnlyObservableCollection reflects those changes. To be notified of the changes to this class, subscribe to the CollectionChanged or PropertyChanged event.

You can simply expose this ReadOnlyObservableCollection and make changes to the underlying collection and rest assured that your view is getting updated.

Upvotes: 0

Brynn McCullagh
Brynn McCullagh

Reputation: 4143

You can just use ToList() to return a copy of the list instead. Other classes can do what they like to the copy it won't alter your original list.

You should make it clear in the name that they are getting a copy.

using System.Linq;

public class WhatClass
{
    List<SomeOtherClass> _SomeOtherClassItems;

    public List<SomeOtherClass> SomeOtherClassItems { get { return _SomeOtherClassItems.ToList(); } }
}

Upvotes: 0

Bryan Watts
Bryan Watts

Reputation: 45445

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.

Upvotes: 40

SLaks
SLaks

Reputation: 887375

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; }
}

Upvotes: 8

Quartermeister
Quartermeister

Reputation: 59139

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.

Upvotes: 6

Marcel Jackwerth
Marcel Jackwerth

Reputation: 54762

How about using AsReadOnly()? - MSDN Documentation

Upvotes: 3

Related Questions