The Incomparable Mr Skeet mentions how irksome it is that collection initializers assume that the collection is mutable.
Suppose we defined an "immutable-yet-initializable collection" as any type that supports this interface:
public interface IImmutableCollection<TCollection, TItem> : IEnumerable<TItem> where TCollection : IImmutableCollection<TCollection, TItem>, new() { TCollection Add(TItem item); }
In other words, it can be enumerated, it can be default-constructed (to get an empty list) and it has an Add method that returns a new list with the item added to it.
And suppose our simple demo implementation of that is as follows:
public class SimpleImmutableList<TItem> : IImmutableCollection<SimpleImmutableList<TItem>, TItem> { private SimpleImmutableList<TItem> _next; private TItem _head; public SimpleImmutableList<TItem> Add(TItem item) { return new SimpleImmutableList<TItem> { _next = this, _head = item }; } public IEnumerator<TItem> GetEnumerator() { for (SimpleImmutableList<TItem> i = this; i._next != null; i = i._next) yield return i._head; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } }
Not very sophisticated, doesn't attempt to solve the well-known "OMG! my list is backward" problem. But anyway. Our problem is that we want this delightful experience:
List<string> mutableList = new List<string> { "First", "Second", "Third" }; Debug.Assert(mutableList.Count() == 3); Debug.Assert(mutableList.ElementAt(2) == "Third");
Except that we want to make a SimpleImmutableList<string> instead of a List<string>. Well, we can get pretty close:
SimpleImmutableList<string> immutableList = new Initializer<SimpleImmutableList<string>, string> { "First", "Second", "Third" }.Result; Debug.Assert(immutableList.Count() == 3); Debug.Assert(immutableList.ElementAt(2) == "First"); // OMG! etc
I've picked out in red ink the extra noise we have to add, but it's not that bad. The enabling thing is that Initializer class:
public class Initializer<TCollection, TItem> : IEnumerable<TItem> where TCollection : IImmutableCollection<TCollection, TItem>, new() { public Initializer() { Result = new TCollection(); } public void Add(TItem item) { Result = Result.Add(item); } public TCollection Result { get; private set; } public IEnumerator<TItem> GetEnumerator() { return Result.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return Result.GetEnumerator(); } }
So if anyone tells you that it is impossible to initialize an immutable collection with a collection initializer, tell them about the time you met a crazy old man with some wild ideas about an Initializer class, and see what they say1.
1. "Get out of my house".
No comments:
Post a Comment