views:

88

answers:

3

I have a use case that I've never really dealt with before. I have an object that after the user clicks "Save" should not be changed. Initially I created two objects, DraftObject and SavedObject. For the latter I created a constructor that only accepted the DraftObject and set each property as protected set.

This works but does not seem ideal. It seems like I should be able to set a property on my object that controls whether the other fields are editable. What's the best way to do this? Forgive my pseudo-code, but this is what I was toying with:

public class MySpecialObject {
        public virtual string MyProperty { get { return MyProperty; } 
            set {
                if (State == "Locked")
                {
                    return;
                } else
                {
                    MyProperty = MyProperty;
                }
            }
         public virtual string State { get { return State; }
            set {
                 if (State == "Locked")
                 {
                     return;
                 } else
                 { 
                   State = State;
                 }
            }

This seems ugly, especially if I have to do this to every single property in my class. There's got to be a better way to do this, any ideas?

+2  A: 

This is actually a fairly interesting issue, because there's a lot to be said for objects that are either immutable, or that start off mutable but can be locked down to become immutable.

Addressing your question in specific, I think it may make more sense to throw an InvalidOperationException if there's an attempt to set a locked property. I don't know of any cleaner or better way.

edit

In a parallel post, JLWarlow pointed out that this is exactly how SecureString works when you call MakeReadOnly.

Steven Sudit
+1  A: 

If its only a function or two for simplicity sake just do it like you wrote if it you do it often it may be a good place to use AOP

http://en.wikipedia.org/wiki/Aspect-oriented_programming

You would then write an aspect that prevents changes when the item is in a certain state.

Another option which is almost as verbose as your code but mayby a bit nicer is to have to use the State design pattern, where you have an abstract class with different implementations per state, so the locked state class would not allow any changes

http://en.wikipedia.org/wiki/State_pattern

Sruly
That article lists nearly a dozen different aspect-oriented frameworks for .NET. Any one in particular that you would recommend?
Steven Sudit
Spring.Net has a large user base and is based on the Java Spring frameworkHere is an article that compares frameworkshttp://www.codeproject.com/KB/cs/AOP_Frameworks_Rating.aspx
Sruly
Interesting article, although it looks like it favors PostSharp over Spring.Net. Anyhow, +1 for the relevant research and good ideas.
Steven Sudit
The article places PostSharp higher but Spring.Net is better known therefore easier to learn.
Sruly
A: 

I know your sample is pseudocode, but maybe you can use a boolean property IsLocked and write it simpler:

set {
          _MyProperty = IsLocked? _MyProperty:value;    
  }
Jonathan
Simpler here, more complicated elsewhere. The setter for `State` would have to check for `value` being `"Locked"` and then set `IsLocked`. Still, it might be a little faster for the other props.
Steven Sudit
`IsLocked` can itself be a property getter.
Ben Voigt
@Ben: Well, I would think it might work better as a method, much like `SecureString.MakeReadOnly`. I say this because it would never be possible to set `IsLocked` to `false`. It might still be useful to have an `IsReadOnly` prop that is itself read-only. In any case, the OP is about locking based on a state, not an explicit lock.
Steven Sudit
@Steven: What do you think a property getter does, besides calculate the value of a property based on the current state of the object?
Ben Voigt
@Ben: Ok, if `IsLocked` just returns `(State == "Locked")` then this is a good idea. However, Jonathan didn't specify a getter, just a property, which implies a boolean backing field. Such a field would not be a good idea.
Steven Sudit
@Ben: It's a simple misunderstanding, and as much my fault as yours. Let's not sweat it.
Steven Sudit