Faking Immutability with Extension Methods
I've been reading Eric Lippert's series on immutable collections (start here with part one) over on his blog, Fabulous Adventures in Coding. I don't understand everything he writes, but it's still a fascinating read.
This morning on my commute I was wondering whether it would be possible to "fake" immutability in an existing list class by adding a "With" and "Without" method. The idea would be to ask for a new list which is the same as the original list but "with" a particular item, or to ask for a new list the same as the original "without" one of its items.
Here's some code to illustrate what I mean:
List<int> nums = new List<int>() { 1, 2, 3, 4, 5 }; foreach (int i in nums.With(6)) { Console.WriteLine(i); } foreach (int i in nums.Without(3)) { Console.WriteLine(i); }
The idea is that the two "foreach" loops are acting on a completely separate object to the original "nums" list. Calling "With" and "Without" constructs a new list rather than modifying the original.
As it turns out this sort of thing is pretty easy to do with extension methods, and I even found a way to make them work on any class that implements IList<T>. Here's my code:
public static class ListExtensions { public static TList With<TList, T>(this TList list, T item) where TList : IList<T>, new() { TList l = new TList(); foreach (T i in list) { l.Add(i); } l.Add(item); return l; } public static TList Without<TList, T>(this TList list, T item) where TList : IList<T>, new() { TList l = new TList(); foreach (T i in list.Where(n => !n.Equals(item))) { l.Add(i); } return l; } }
This is probably not the most efficient way to do it, and I'm not suggesting for a second that someone would start bandying the term "immutable" about when referring to a plain old IList<T>, but I still thought it was an interesting thought experiment into what's possible with extension methods.
Comments
# Eric Lippert
15/07/2008 3:18 AM
Glad you like the blog.
If you restrict yourself to IEnumerable of T then you can do With and Without very efficiently. To do with, just foreach over the sequence, yielding each one, and then yield the new item. To do without, just do a Where and pass a predicate that filters out the items you do not want.
# mabster
15/07/2008 8:14 AM
Hi Eric,
Yeah, IEnumerable<T> would be a much better way to do this. It's funny to think that this post was only written in January, yet I feel like I was still very much a beginner at that stage when it comes to extension methods etc. I'd know better now!
Thanks for the comment!