views:

123

answers:

6

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?

+1  A: 

I would use a wrapper class that keeps everything read-only. This is for scalability, reliability and general readability.

I do not foresee any other methods of doing this that will provide the above three mentioned benefits as well as something more. Definitely use a wrapper class here in my opinion.

Kyle Rozendo
+2  A: 

Why not something like:

private int someValue;
public int SomeValue
{
    get
    {
         return someValue;
    }
    set
    {
         if(ReadOnly)
              throw new InvalidOperationException("Object is readonly");
         someValue= value;
    }
Oskar Kjellin
Because it won't work (as expected) for reference types. Your code is equivalent to readonly keyword only it is more obscure.
macias
Why would it be equivalent to readonly keyword? This is dynamic, readonly keyword is not.
Oskar Kjellin
A: 

Would something like this help:

class Class1
{
    private bool _isReadOnly;

    private int _property1;
    public int Property1
    {
        get
        {
            return _property1;
        }
        set
        {
            if (_isReadOnly) 
              throw new Exception("At the moment this is ready only property.");
            _property1 = value;
        }
    }
}

You need to catch exceptions when setting properties.

I hope this is something you are looking for.

That would always throw exceptions, you should never throw an instance of Exception by the way.
Oskar Kjellin
@Oskar True, I changed it.
A: 

You can not get compile-time checks (like given with the keyword readonly) by changing a property to readonly at runtime. So there is no other way, as to check manually and throw an exception.

But propably it is better to re-design access to the class. For example create a "writer class", which checks if the underling "data class" can currently be written or not.

Danvil
+5  A: 

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.

LBushkin
Thanks! Unfortunately option 2 does not work for me because the property grid will use reflection to figure out the real properties behind an interface are in fact not read only. So it seems that option 3 is the best answer for me. Although I do like your suggestion of combining options 2 and 3.
Eric Anastas
A: 

You can use PostSharp to create OnFieldAccessAspect that will not pass new value to any field when _readOnly will be set to true. With aspect code repetition is gone and there will be no field forgotten.

Karel Frajtak