How can I make a read only version of a class?

Eric Anastas picture Eric Anastas · Apr 27, 2010 · Viewed 16.6k times · Source

I have a class with various public properties which I allow users to edit through a property grid. For persistence this class is also serialized/deserialized to/from an XML file through DataContractSerializer.

Sometimes I want to user to be able to save (serialize) changes they've made to an instance of the class. Yet at other times I don't want to allow the user to save their changes, and should instead see all the properties in the property grid as read only. I don't want to allow users to make changes that they'll never be able to save later. Similar to how MS Word will allow users to open documents that are currently opened by someone else but only as read only.

My class has a boolean property that determines if the class should be read-only, but is it possible to use this property to somehow dynamically add a read-only attributes to the class properties at run-time? If not what is an alternative solution? Should I wrap my class in a read-only wrapper class?

Answer

LBushkin picture LBushkin · Apr 27, 2010

Immutability is an area where C# still has room to improve. While creating simple immutable types with readonly properties is possible, once you need more sophisticated control over when type are mutable you start running into obstacles.

There are three choices that you have, depending on how strongly you need to "enforce" read-only behavior:

  1. Use a read-only flag in your type (like you're doing) and let the caller be responsible for not attempting to change properties on the type - if a write attempt is made, throw an exception.

  2. Create a read-only interface and have your type implement it. This way you can pass the type via that interface to code that should only perform reads.

  3. Create a wrapper class that aggregates your type and only exposes read operations.

The first option is often the easiest, in that it can require less refactoring of existing code, but offers the least opportunity for the author of a type to inform consumers when an instance is immutable vs when it is not. This option also offers the least support from the compiler in detecting inappropriate use - and relegates error detection to runtime.

The second option is convenient, since implementing an interface is possible without much refactoring effort. Unfortunately, callers can still cast to the underlying type and attempt to write against it. Often, this option is combined with a read-only flag to ensure the immutability is not violated.

The third option is the strongest, as far as enforcement goes, but it can result in duplication of code and is more of a refactoring effort. Often, it's useful to combine option 2 and 3, to make the relationship between the read-only wrapper and the mutable type polymorphic.

Personally, I tend to perfer the third option when writing new code where I expect to need to enforce immutability. I like the fact that it's impossible to "cast-away" the immutable wrapper, and it often allows you to avoid writing messy if-read-only-throw-exception checks into every setter.