A friend recently asked for suggestions about creating a class that is mutable up to a point (while it’s being constructed), and then immutable once clients begin to use it. His solution was to add a flag that could be set to make the thing read-only. For example, the class below is mutable up to the point when the _inUse
field is set.
public static class LabelsArray
{
private static Dictionary<int, string> _items = new Dictionary<int,string>();
private static bool _inUse = false;
public static void SetInUse()
{
_inUse = true;
}
public static void LoadFromFile(string f)
{
if (_inUse)
throw new ApplicationException("no modifications allowed.");
// load labels from a file
}
public static void AddLabel(int key, string val)
{
if (_inUse)
throw new ApplicationException("no modifications allowed.");
// add a label
}
public static string this[int key]
{
get { return _items[key]; }
}
public static bool TryGetValue(int key, out string val)
{
return _items.TryGetValue(key, out val);
}
}
Code that uses this class would initialize it by calling LoadFromFile
and AddKeyValue
as appropriate and, before creating any objects that use the labels, would call SetInUse
to prevent it from being modified. Something like this:
LabelsArray.LoadFromFile("foo");
LabelsArray.AddKeyValue("joe", "programmer");
LabelsArray.SetInUse();
// The object can no longer be modified
His application was somewhat more complex than that, but that’s essentially what he described.
In a program that only needs one LabelsArray
, this model works well. It’s quick to implement and does the job. It’s not a pattern you’d want to use if you have multiple labels arrays, though, unless you want to duplicate all that code for every different array. Also, that _inUse
flag is kind of messy. Wouldn’t it be nice if you could implement this without having to resort to a flag?
How about writing code that constructs a dictionary and then wraps it in a read-only container? Something like this:
public ReadOnlyDictionary<int, string> CreateLabelsArray()
{
var dict = new Dictionary<int, string>();
// code here loads data from file,
// adds other key/value pairs, etc.
// And, finally:
return new ReadOnlyDictionary<int, string>(dict);
}
Clients make a single call to create the dictionary and get a read-only reference to it:
var myLabels = CreateLabelsArray();
ReadOnlyDictionary creates a read-only wrapper around the dictionary that you pass to the constructor. Because the CreateLabelsArray
method created the original dictionary and only the ReadOnlyDictionary
instance maintains a reference to it, there is no way that the dictionary can be modified.
I like this solution because it eliminates the flag, and I can easily create multiple labels arrays. If I really need that labels array to be static, I can declare a static reference and avoid the static class. In my experience, you should be careful with static classes.
If the collection you’re working with isn’t a dictionary, you might need a ReadOnlyCollection (essentially an immutable List<T>
) rather than a ReadOnlyDictionary
.
Those two collections are very useful in ensuring that critical data is not inadvertently modified. You’ll be surprised by how many problems you can solve by using these two collection types with a technique similar to what I showed above. If ReadOnlyDictionary
and ReadOnlyCollection
don’t quite fit your requirement, it’s pretty easy to create your own class that works in much the same way.
In the example I showed above, the dictionary returned by CreateLabelsArray
is guaranteed to be thread-safe because there is no possible way that the underlying dictionary can be modified. Clients can query it with impunity. If instead you were to write code that maintains a reference to the underlying mutable dictionary, then you have a potential problem. Clients that reference the ReadOnlyDictionary
can’t modify it, but they might fail if some other part of the code that has a reference to the underlying dictionary modifies the dictionary concurrently with clients’ queries.
If you’re going to use ReadOnlyDictionary
or ReadOnlyCollection
, especially in multithreaded code, I strongly recommend that you use the model I showed above: create the underlying collection, instantiate the read-only version, and then discard your reference to the mutable collection. If you maintain a reference to the mutable collection, you could inadvertently modify it and create a problem that is often difficult to track down.