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