There are a couple of similar questions already on SO, but none of the ones I found really touches on this particular topic, so here it goes...
My understanding is that one should always attempt to return an interface over a concrete class. Won't go into the reasons behind it, plenty of things on SO about that already.
However in the case of an IReadOnlyCollection vs a ReadOnlyCollection I'm not sure if that rule should be followed.
An IReadOnlyCollection can be easily cast into a List, which... well... breaks the ReadOnly aspect that the contract promises.
ReadOnlyCollection however cannot be cast into a List, but it means returning a concrete class.
In the long run, does it actually matter? It seems to me like in most cases a ReadOnly*/IReadOnly* object is only returned returned by either a method or a read-only property.
So even if the user decides to cast it to something else (in the case of a IReadOnly* object) or use LINQ to create a collection of some kind out of it (in the case of ReadOnly* object), there's really no way that the class exposing the ReadOnly*/IReadOnly* object is going to accept that back.
So what's the recommendation here, return an IReadOnly* interface or a concrete ReadOnly* class instance?
An instance of the ReadOnlyCollection<T> generic class is always read-only. A collection that is read-only is simply a collection with a wrapper that prevents modifying the collection; therefore, if changes are made to the underlying collection, the read-only collection reflects those changes.
It demonstrates that IReadOnlyList<T> can change even during duration of a single method! Remember not to use ReadOnlyCollection<T> class to create a defensive copy. This class is only a wrapper - it does not copy the actual data. These types are truly immutable, they never change their contents after they are created.
IReadOnlyCollection<T> can only be cast to List<T> if the underlying object is of that type. ReadOnlyCollection<T> for example also implements IReadOnlyCollection<T>.
So my recommendation, return IReadOnlyCollection<T> and if you are worried that caller would wrongly cast it to something it shouldn't, make sure the underlying type is ReadOnlyCollection<T>
public IReadOnlyCollection<User> GetUsers()
{
return new ReadOnlyCollection<User>();
}
But returning IReadOnlyCollection<T> should be enough for caller of function to understand it is supposed to be read only.
Note that you can never completely secure your code with a ReadOnlyCollection<T>, caller can still use reflection to access the internal list and manipulate it.
The only option in that case would be to create a copy if the list and return that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With