I am doubting my understanding of the System.Collection.Generic.IReadOnlyCollection<T>
semantics and doubting how to design using concepts like read-only and immutable. Let me describe the two natures I'm doubting between by using the documentation , which states
Represents a strongly-typed, read-only collection of elements.
Depending on whether I stress the words 'Represents' or 'read-only' (when pronouncing in my head, or out loud if that's your style) I feel like the sentence changes meaning:
IReadOnlyCollection<T>
makes clear to the user (i.e. someone that codes against the declaring type) that he may not modify this collection. However, it is ambiguous in whether the declaring type itself may modify the collection. The first option is actually my preferred interpretation, although this contract can easily be broken, e.g. by constructing a ReadOnlyCollection<T>
from an array of T
's and then setting a value into the wrapper array.
The BCL has excellent interfaces for facade immutability, such as IReadOnlyCollection<T>
, IReadOnlyList<T>
and perhaps even IEnumerable<T>
, etc. However, I find observational immutability also useful and as far as I know, there aren't any interfaces in the BCL carring this meaning (please point them out to me if I'm wrong). It makes sense that these don't exist, because this form of immutability cannot be enforced by an interface declaration, only by implementers (an interface could carry the semantics though, as I'll show below). Aside: I'd love to have this ability in a future C# version!
Example: (may be skipped) I frequently have to implement a method that gets as argument a collection which is used by another thread as well, but the method requires the collection not to be modified during its execution and I therefore declare the parameter to be of type IReadOnlyCollection<T>
and give myself a pat on the back thinking that I've met the requirements. Wrong... To a caller that signature looks like as if the method promises not to change the collection, nothing else, and if the caller takes the second interpretation of the documentation (facade) he might just think mutation is allowed and the method in question is resistant to that. Although there are other more conventional solutions for this example, I hope you see that this problem can be a practical problem, in particular when others are using your code (or future-you for that matter).
So now to my actual problem (which triggered doubting the existing interfaces semantics):
I would like to use observational immutability and facade immutability and distinguish between them. Two options I thought of are:
IImmutableCollection<T> : IReadOnlyCollection<T> { }
and IImmutableList<T> : IReadOnlyList<T> { }
. Note that the interfaces don't have any members except for the inherited ones. The purpose of these interfaces would be to solely say "Even the declaring type won't change me!"‡ I specifically said "won't" here as opposed to "can't". Herein lies a disadvantage: an evil (or erroneous, to stay polite) implementer isn't prevented from breaking this contract by the compiler or anything really. The advantage however is that a programmer who chose to implement this interface rather than the one it directly inherits from, is most likely aware of the extra message sent by this interface, because the programmer is aware of the existence of this interface, and is thereby likely to implement it accordingly. I'm thinking of going with the second option but am afraid it has design issues comparable to those of delegate types (which were invented to carry semantic information over their semanticless counterparts Func
and Action
) and somehow that failed, see e.g. here.
I would like to know if you've encountered/discussed this problem as well, or whether I'm just quibbling about semantics too much and should just accept the existing interfaces and whether I'm just unaware of existing solutions in the BCL. Any design issues like those mentioned above would be helpful. But I am particularly interested in other solutions you might (have) come up with to my problem (which is in a nutshell distinguishing observational and facade immutability in both declaration and usage).
Thank you in advance.
† I'm ignoring mutations of the fields etc on the elements of the collection.
‡ This is valid for the example I gave earlier, but the statement is actually broader. For instance any declaring method won't change it, or a parameter of such a type conveys that the method can expects the collection not to change during its execution (which is different from saying that the method cannot change the collection, which is the only statement one can make with existing interfaces), and probably many others.
An interface cannot ensure immutability. A word in the name wont prevent mutability, that's just another hint like documentation.
If you want an immutable object, require a concrete type that is immutable. In c#, immutability is implementation-dependant and not visible in an interface.
As Chris said, you can find existing implementations of immutable collections.
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