Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#
Article

Immutable Generic Collection Extensions

Rate me:
Please Sign up or sign in to vote.
3.33/5 (5 votes)
18 Dec 2007CPOL3 min read 39.5K   114   17   7
Create immutable / read-only collections from existing ICollection, IList, or IDictionary instances.

Introduction

The System.Collections.Generic classes enable you to easily create typed data structures. However, it is missing generic classes that allow you to create immutable / readonly collection instances. In this article, I'll describe how to easily create an immutable collection from an existing generic instance, using C# 3.0 extension methods.

Background

Immutable collections are instances of collection classes whose contents cannot be modified. Immutability is required in certain programming scenarios where the contents of the collection must be static to ensure that it can be "safely" used by any consumer without modifying the state of the program.

Generic collection classes were introduced in the .NET framework 2.0 System.Collections.Generic namespace. The generic collection classes enabled developers to easily create typed collections, whereas in .NET 1.x, you either had to use untyped collections or create a custom collection with typed methods.

Unfortunately, for most generic collections, .NET 2.0 does not contain corresponding immutable generic classes or methods. For example, if you create a typed System.Collections.Generic.List<T>, then the List.Add<T>( T typedObject ) method is automatically created and cannot be overridden.

As a solution, it is possible to create a custom version of a generic collection interface which creates a facade for an existing mutable instance. The facade class can implement the same collection interface as the class it wraps, but will throw an exception when any of its mutator methods are called. The facade class can then be used like so:

C#
using System.Collections.Generic;
using Com.WickedByte.Collections.Generic;

IList<string> mutable = new List<string>();
mutable.Add( "Hello " );

// public class ImmutableList<T> : IList<T>{...}
IList<string> readOnly = new ImmutableList<string>( mutable );

//By design, the next call throws an exception.
readOnly.Add( "World!" );

This approach works, but is rather unwieldy, because it requires developers to be familiar with each of the custom immutable classes that you create.

Fortunately, C# 3.0 introduces an elegant way to solve this problem by introducing extension methods. Extension methods allow you to add methods to any existing class without modifying the source of the existing class. In the C# 3.0 approach, I've created immutable wrapper classes that implement ICollection<T>, IList<T>, and IDictionary<TKey,TValue>. However, I've made their visibility internal, so that developers are not aware of the custom implementations. Instead of publically exposing the custom classes, I've created an extension method for each collection interface, called ToReadOnly(). Developers need only to import the correct namespace, and will then be able to create an immutable instance with a single method call on the existing mutable generic instance.

Using the code

To use the code, import the Com.WickedByte.Collections.Generic namespace, and then call ToReadOnly() on any instance of ICollection<T>, IList<T>, or IDictionary<TKey,TValue>.

C#
using Com.WickedByte.Collections.Generic;
using System.Collections.Generic;

IList<string> helloWorld = new List<string>();
helloWorld.Add( "Hello " );
IList<string> justHello = helloWorld.ToReadOnly();

//throws ReadOnlyException
justHello.Add( "World!" );

It's important to note that if you retain a reference to the original mutable collection instance, then the immutable collection can still be modified indirectly.

I've only created extension methods for the three main collection interfaces: ICollection<T>, IList<T>, and IDictionary<TKey,TValue>. You may wish to create your own extension methods for classes such as Stack<T> or HashSet<T>. When designing your facade class, it's important to notice whether any of the interface methods or properties return another collection. To preserve the integrity of the parent class, the returned collection must also be immutable. For example, in designing the immutable version of IDictionary<TKey,TValue>, I noted that both the Keys and Values properties returned an ICollection<T>. I simply returned an instance of my custom immutable ICollection<T> for each of these properties.

History

  • Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) WickedByte Software
United States United States
Marshall's torrid relationship with programming started as a child using BASIC on a Commodore PET computer in the 70's. He continued programming through high school, but did not study Computer Science in college. At the time, compilers would fail without telling you why, so after much soul searching, he realized he didn't want to make a living by spending eight hours a day looking for a missing semi-colon.

By the time he was pursuing his Ph.D. in Communication and Marketing, Microsoft had released Visual Studio. The improvements in the IDE were enough to cause Marshall to have late night affairs with COM and ASP. Marshall spent the dotcom bubble years as a web developer. After the bubble burst, he worked independently as a Java developer for medical applications. When Microsoft released an early beta of the .NET Framework, he was convinced to switch his focus from the Java Platform to the new Framework. He spent some time at Philips Medical Systems writing the data-access layer for the Carevue Chart hospital system. He is currently Technical Director for ASE Technologies.

Marshall lives in Salem, Massachusetts but would rather be in Hawaii.

Comments and Discussions

 
QuestionDesign [modified] Pin
Andrew Shapira5-May-08 18:20
Andrew Shapira5-May-08 18:20 
GeneralReadOnlyDictionary<tkey, tvalue=""></tkey,> Pin
The .NET Junkie13-Dec-07 10:00
The .NET Junkie13-Dec-07 10:00 
GeneralRe: ReadOnlyDictionary [modified] Pin
Marshall Rosenstein14-Dec-07 5:23
Marshall Rosenstein14-Dec-07 5:23 
GeneralRe: ReadOnlyDictionary Pin
The .NET Junkie14-Dec-07 23:00
The .NET Junkie14-Dec-07 23:00 
GeneralRe: ReadOnlyDictionary Pin
Marshall Rosenstein18-Dec-07 4:48
Marshall Rosenstein18-Dec-07 4:48 
GeneralNET 2.0 provides ReadOnlyCollection&lt;T&gt Pin
OrlandoCurioso13-Dec-07 5:48
OrlandoCurioso13-Dec-07 5:48 
GeneralRe: NET 2.0 provides ReadOnlyCollection&lt;T&gt [modified] Pin
Marshall Rosenstein13-Dec-07 6:12
Marshall Rosenstein13-Dec-07 6:12 

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.