Here the author, in theory, tries to follow the principle of least knowledge and does not expose the details of implementation.
Perhaps this code is old or all components are not shown, but in this implementation the design is not good.
CategoryID property is mutable. The client can write there a null followed by a raking of a NullReferenceException .
The property only tries to hide implementation details, but does it poorly. Everything strongly depends on the context, but there are at least two ways to make this design more rigid on the one hand and simpler on the other.
First, you can make the type immutable and get the collection in the constructor. In this case, the CategoryID property instead of the IReadOnlyCollection type can become an IReadOnlyCollection or an IReadOnlyList .
Secondly, if a class cannot be made immutable, then it makes sense to add the AddCategory method and still make the CategoryID property type IReadOnlyCollection/IReadOnlyList .
It is difficult to speak here without context, but I am always amazed by such data objects in namespaces named Model . The model in the namespace name tells me that the whole essence of the application, its domain objects, with the behavior and all kinds of bells and whistles will be hidden here. And when I see simple data objects in such a namespace, I have some mismatch of expectations with reality.
In other words, if there is a desire to create models, then it makes sense to hide the insides fully, rather than removing the "real type of list." Then it will be possible to add higher-level behavior (some sort of filtering logic by categories and something else) without breaking existing customers.
And now a little on the topic:
The interfaces of the collections in BCL are a little crazy in the sense that it is now very difficult to say what they mean. This is especially true for the ICollection type: what is this collection? Is it mutable? It seems to be yes, there is a method Add . But the trouble is, arrays also implement ICollection<T> , whose Add method throws an exception. Yes, the IsReadOnly property is IsReadOnly , but are all the clients checking it for sure?
Well, of course, the collections have Contains and Remove methods, but the first one is O (N), which is almost always bad, and the second one also does not work for all collections.
It turns out that this interface is often used as an IEnumerable + Count , but in this case IReadOnlyXXX views are also better IReadOnlyXXX .
As a conclusion: you need to understand what and from whom you are hiding and whether you are hiding anything at all. If this code is used in applications, then there are two options: use a specific collection, if the class is a data repository or hide the collection fully and expose an IReadonlyXXX representation with specialized Add methods.