Click here to Skip to main content
15,889,462 members
Articles / Programming Languages / C#

Immutable Collections Should Not Implement Mutable Interfaces

Rate me:
Please Sign up or sign in to vote.
3.91/5 (6 votes)
17 Sep 2013LGPL32 min read 15.9K   1   18
This post tells us why immutable collections should not implement mutable interfaces.

I just read the new blog post about .NETs new mutable collections. It mostly looks really nice, but there is a major problem: They implement mutable interfaces like IList<T> which has an Add() method.

The blog post states this:

Implementing the mutable interfaces

Several people raised the issue that our immutable collection types implement the mutable interfaces. For example, this is what the declaration of ImmutableList<t> looks like:

C#
public sealed class ImmutableList<T> :
        IImmutableList<T>,
        IEnumerable,
        IEnumerable<T>,
        IReadOnlyCollection<T>,
        IReadOnlyList<T>,
        ICollection,
        ICollection<T>,
        IList,
        IList<T>
{
   ...
}

It’s worth pointing out that this declaration does not have a correctness issue – you cannot mutate an instance of ImmutableList<t> by casting it to a mutable interface. For example, calling IList<T>.Add would throw NotSupportedException<t>.

Now, why is that such a big issue?

shock

Well. It’s not (in a strict sense). I thought so first. When I saw the throw NotSupportedException(); my internal alert system was blinking “LSP violation” in red and the siren was going all crazy.

But then I started to read more carefully about the interfaces in MSDN. The documentation for the Add() method state that NotSupportedException can be thrown if the collection is read only. It’s therefore not a Liskovs Substitution Principle violation.

You could argue that any interface containing an Add() method is incorrectly defined if that Add() method may not be used for all implementations. It would have been better with an interface like IReadOnlyList that exposes an item accessor.

C#
public interface IReadOnlyList<T> : IEnumerable<T>
{
    T this[int index] { get; }
    int Count { get; }
}

public interface IList<T> : IReadOnlyList<T>
{
    T this[int index] { get; set;  }
    void Add(T item);
    void Remove(T item);
}

(I’m not satisfied with the naming as ‘is-a’ relationships also applies to interfaces, but you might understand what I’m going for.)

Ask yourself. If you got a list through a method like IList<User> GetUsers();, would you ever think that you may NOT use the Add/Remove methods? No, you wouldn’t as the defacto standard has been to expose those through the IEnumerable<T> interface. IEnumerable<T> isn't a good choice in all situations, but it's the best choice that .NET provides for read-only collections.

The implication of using the mutable interfaces for the immutable types is that our code would have to be something like this when we want to get a mutable collection:

C#
public void ProcessUsers(IList<T> users)
{
    if (myList.IsReadOnly)
        throw new InvalidOperationException
        ("We require a proper list, one were the Add method works");

    // [...]
}

As of now, there is no interface that we can use to communicate that we need to work with mutable collections. And no, using concrete types is not an option. Understand? You force us to either start using our own collection interfaces or resort to runtime errors.

The only reason to the decision that I could find is the following:

However, we received the feedback that people had a hard time figuring out how to pass an immutable list to an existing method that took, for example, IList<t>.

Ask yourself WHY they used IList<T> when an immutable object is required. They could have simply used IEnumerable<T>. My best bet is that they required something that they can traverse multiple times or wanted to know the length of the list. As in such, the real solution would probably have been to create something like IReadOnlyList<T>.

Please .NET team, do not use the mutable interfaces, even if it’s technically valid to do so.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Founder 1TCompany AB
Sweden Sweden

Comments and Discussions

 
GeneralMy vote of 2 Pin
Qwertie24-Sep-13 21:25
Qwertie24-Sep-13 21:25 
GeneralThughts Pin
PIEBALDconsult23-Sep-13 15:09
mvePIEBALDconsult23-Sep-13 15:09 
QuestionThere is a perfectly reasonable explanation for this. Pin
Qwertie23-Sep-13 12:58
Qwertie23-Sep-13 12:58 
AnswerRe: There is a perfectly reasonable explanation for this. Pin
jgauffin23-Sep-13 19:23
jgauffin23-Sep-13 19:23 
Qwertie wrote:
Consequently, virtually all existing methods that need read-only access to a list take an argument of type IList<T>. If Microsoft's immutable collections did not implement IList<T>, you would be unable to pass an immutable collection to any of these (thousands? millions?) of existing methods that expect an IList<T>.


That's under the assumption that all of those lines require an immutable collection. I've actually talked with Microsoft about this and they said that about 50% of the usages of IList is like a mutable collection.

They did however not say how many of the other 50% would have been using IReadOnlyCollection instead (if it there had existed in earlier versions of the framework). i.e. they required a read-only interface that could be traversed multiple times and/or had an indexer + count.

However, its' quite easy to create an adapter (adapter pattern) which takes a IImmutableList and exposes it as an IList. By doing so they could try to save the situation by not implementing IList in the new immutable collections.


Qwertie wrote:
Of course, Microsoft could provide an AsList() extension method that converts IReadOnlyList<T> to IList<T> by creating a wrapper object. But this would reduce performance and increase memory usage. Since IList<T> is explicitly documented as supporting immutable lists (by throwing an exception when you call a mutating method), it makes sense to just implement IList<T> directly.


No need to create a new list, just use the adapter pattern as I said above. The memory/performance impact is minimal.

Qwertie wrote:
Microsoft is not making a mistake in their new design. The mistake already happened years ago when they decided not to offer read-only interfaces in the first place.


Yes, the mistake was made a long time ago. It's therefore time to try to make it better and not worse.
GeneralRe: There is a perfectly reasonable explanation for this. Pin
Qwertie24-Sep-13 5:15
Qwertie24-Sep-13 5:15 
GeneralRe: There is a perfectly reasonable explanation for this. Pin
jgauffin24-Sep-13 9:47
jgauffin24-Sep-13 9:47 
GeneralRe: There is a perfectly reasonable explanation for this. Pin
Qwertie24-Sep-13 14:24
Qwertie24-Sep-13 14:24 
GeneralRe: There is a perfectly reasonable explanation for this. Pin
jgauffin24-Sep-13 18:32
jgauffin24-Sep-13 18:32 
GeneralMy vote of 5 Pin
stefanveliki17-Sep-13 6:49
stefanveliki17-Sep-13 6:49 
GeneralMy vote of 2 Pin
William E. Kempf17-Sep-13 4:59
William E. Kempf17-Sep-13 4:59 
GeneralRe: My vote of 2 Pin
jgauffin17-Sep-13 5:05
jgauffin17-Sep-13 5:05 
GeneralRe: My vote of 2 Pin
William E. Kempf17-Sep-13 5:27
William E. Kempf17-Sep-13 5:27 
GeneralRe: My vote of 2 Pin
jgauffin17-Sep-13 6:20
jgauffin17-Sep-13 6:20 
GeneralRe: My vote of 2 Pin
William E. Kempf17-Sep-13 6:41
William E. Kempf17-Sep-13 6:41 
GeneralRe: My vote of 2 Pin
jgauffin17-Sep-13 7:47
jgauffin17-Sep-13 7:47 
GeneralRe: My vote of 2 Pin
William E. Kempf17-Sep-13 9:08
William E. Kempf17-Sep-13 9:08 
GeneralRe: My vote of 2 Pin
jgauffin17-Sep-13 9:12
jgauffin17-Sep-13 9:12 
GeneralMy vote of 5 Pin
fireOut17-Sep-13 1:55
fireOut17-Sep-13 1:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.