I don't know if this is possible, but basically, I want to expose a property of type ReadOnlyCollection<ReadOnlyCollection<MyType>>
, while still being able to modify the underlying collection inside the class exposing the property. Further, I want users of my class to be able to hold a reference to the collection returned, so that it updates as I update it internally in the class exposing the property.
For instance, If this were a single collection, I could do something like this:
public class MyClass {
private List<MyType> _List;
public ReadOnlyCollection<MyType> RList {
get {
return _List.AsReadOnly();
}
}
}
Which would still allow for adding items to the list inside my class by doing _List.Add()
. Further, if a client of this class was to do, say:
var collectionRef = myClassInstance.RList;
Then collectionRef
would also change as I add elements to the list from inside MyClass
.
The problem arises if I want a list of lists:
private List<List<MyType>> _List;
public ReadOnlyCollection<ReadOnlyCollection<MyType>> RList {
get {
return _List.AsReadOnly();
}
}
The immediate problem with the above attempt is that AsReadonly()
only applies to the outer list. So I'd be trying to return a ReadOnlyCollection<List<MyItem>>
which is not the declared type for the property. The only way I can think of to satisfy the declared property type would involve creating a new ReadOnlyCollection
in a way similar to:
new ReadOnlyCollection<ReadOnlyCollection<MyItem>>(_List)
or
new ReadOnlyCollection<ReadOnlyCollection<MyItem>>() {
new ReadOnlyCollection<MyItem>(_List[0]),
new ReadOnlyCollection<MyItem>(_List[1]),
...
}
But I'm not sure what the syntax for creating this nested collection would be; Also, I'm not sure if by creating 'new' collections, I won't be able to track changes to the underlying lists by means of a ref to the value returned by the property. That is, if inside MyClass
I do _List[1].Add(myTypeInstance)
I'm not sure that someone holding a ref to the readonly version will see that new item.
I'm open to other approaches all together honesty. Basically I just need to expose a list of lists of items which is readonly but can be modified inside the class that exposes the property and for clients to be able to see the changes reflected without needing to get the value again from the property accessor.
ReadOnlyCollection
cannot do this, at least not by itself. If you add a new list to _List
, any user of RList
will need to get a chance to see a new ReadOnlyCollection
linked to that new list. ReadOnlyCollection
simply doesn't support that. It is only a read-only view of the original items, and the original items are modifiable.
You can implement a custom class which behaves similarly to ReadOnlyCollection
, except that it doesn't return the original items, but wraps them. ReadOnlyCollection
's implementation is trivial: pretty much all of ReadOnlyCollection
's methods can be implemented in a single line: they either throw an unconditional exception (e.g. IList.Add
), return a constant value (e.g. IList.IsReadOnly
), or forward to the proxied container (e.g. Count
).
The change you would need to make to adapt it to your use involve the various methods that deal directly with items. Note that that is more than simply the this[int]
indexer: you would also need to make sure two proxies of an identical list compare as equal, and provide a private/internal method to obtain the original list from a proxy, to make methods such as Contains
work. You would also need to create a custom enumerator type to make sure GetEnumerator
doesn't start returning the original items. You would need to create a custom CopyTo
implementation. But even keeping those things in mind, it should be quite easy to do.
That said, perhaps there is a somewhat less clean but easier approach: if you create a custom class MyReadOnlyCollection<T>
derived from ReadOnlyCollection<T>
, you can provide an internal Items
member (which forwards to ReadOnlyCollection<T>
's protected Items
member).
You can then create a MyReadOnlyCollection<MyReadOnlyCollection<MyClass>>
, and call RList[0].Items.Add
from your own assembly, without worrying that external users will be able to call that too.
As noted, if the outer list actually never changes, it can be simpler: in that case, you can simply do something like
public ReadOnlyCollection<ReadOnlyCollection<MyType>> RList {
get {
return _List.ConvertAll(list => list.AsReadOnly()).AsReadOnly();
}
}
which doesn't bother to monitor _List
for changes.