If there was no allocation, there'd be no reason to have the AsReadOnly() function.
If all you want to do is cast it to an IReadOnlyList, then do that:
var myList = new List<string>();
var myListReadOnly = (IReadOnlyList<string>) myList;
Casting rather than relying on a function with potential side effects (like creating an allocation) is more explicit about what you are doing.
If anything, I'd say that the AsReadOnly() function's name is a bit misleading, as it does not explicitly mention the use of ReadOnlyCollection.
Speaking on the motivation of this, I believe you are correct. Benefits of returning a ReadOnlyCollection include:
- Prevents receivers of the collection casting it to a mutable list
- Methods gotten via reflection will not work on the collection
- Prevents erroneous pattern matching on .GetType()
The concept of ReadOnly is to protect the programmer from making a mistake manipulating the data. In most applications, the performance of a small allocation like this is trivial and is worth the benefit to not only communicate this data is ReadOnly, but to more actively enforce it.
As applications get more complex, many different code paths can lead to bizarre and unexpected use-cases. Take this example:
static void GatherAnimals()
{
var myAnimals = new List<IAnimal> { new Cat(), new Dog() };
var animalCollector = new MyAnimalCollector();
animalCollector.Collect(myAnimals);
}
class MyAnimalCollector : BaseAnimalCollector
{
// Called by the BaseAnimalCollector.Collect() method
override void AddAnimals(object animals)
{
if (animals is IList<IAnimal> list)
{
list.Add(new Rabbit());
}
}
}
Let's say you as a developer do not have control over the implementation of BaseAnimalCollector, thus you cannot change parameter "animals" to a different type.
Now, somewhere else in your code, you have a slightly different call:
static void GatherAllCats()
{
var myAnimals = new List<IAnimal> { new Cat(), new Tiger() }.AsReadOnly();
var animalCollector = new MyAnimalCollector();
animalCollector.Collect(myAnimals);
}
Now the "is" call will return false, and the list will not be edited.
I would never recommend coding like the above example, but it shows how these kinds of bugs can sneak in.
Let's say it was critically important that a rabbit is not added to the Cat collection: by .AsReadOnly() returning a ReadOnlyCollection, it protects from that happening. If it just did a cast, it would not.
List<T>
andReadOnlyCollection<T>
?IReadonlyCollection<T>
, while.AsReadOnly()
returns aReadOnlyCollection<T>
, which is handy if the function you're trying to call accepts the class rather than the interface.