If something would behave as a read-write property, it should generally be implemented using a read-write property. On the other hand, the fact that the value of a property can be changed does not mean it will behave like a read-write property. I would suggest that in general, something should only be implemented as a read-write property if it acts as a "clean" property (described below), though I would note that many classes in .net use read-write properties in ways which do not conform to what I suggest. I would conjecture that in many cases, this stems from time pressure to get the framework implemented before the full philosophical background was completely worked out.
I would regard a read-write property as clean if:
- Reading it will never have any side effect and will always yield the same value in the absence of a method call or write to that property.
- Reading it and then writing back the value read will have no effect.
- A read which follows a write without intervening method call will always yield the value written.
- Writing the property more than once without any intervening method calls will have the same effect as performing only the last write.
- In the absence of intervening method calls, the effect of setting a clean property to X and then setting some other property to Y will be the same as setting the other property to Y and then setting the clean one to X.
Note that it is perfectly permissible for a write to a "clean" property to affect the state of other read-only properties. The effect of setting multiple properties should depend upon the values written, but not the sequence in which the writes occur. If
myRect is initialized to (X=1,Y=2,Width=3,Height=4), the effect of
MyRect.X=2; MyRect.Top = MyRect.Right; should be the same as
MyRect.X = 2; MyRect.Top = 5; or
MyRect.Top = 5; MyRect.X = 2;, even though it wouldn't be the same as
MyRect.Top = MyRect.Right; MyRect.X = 2;.
Many .net classes use properties in ways which are by these definitions "unclean". Using these definitions, for example, StringBuilder.
Length should probably be a read-only property, but
Text could probably be a read-write property (reading
Text would be equivalent to
ToString(), while writing it would be equivalent to clearing it and doing
There are variety of situations in which one may be able to change properties of an object, but they should not be considered "clean read-write properties". Among them:
- A rectangle which stores (X,Y,Width,Height) and exposes them as read-write properties may have a `Right` property, but attempting to set it would affect one of the others.
- An object may hold a reference to an `IDisposable` object and be expected to possibly call `Dispose` on it when it's no longer needed. A method `SetXXAndTakeOwnership()` would offer clearer semantics (call `Dispose` the old object before setting the property) would offer much clearer semantics than a read-write property.
- Some objects may have properties which can only be set to certain combinations of values; having a method to set all such properties at once may be much nicer than having to set the properties individually and worry about sequencing. `ProgressBar` is particularly icky in this regard, since its properties must be written in an order which depends upon old and new values; if a `ProgressBar` is presently indicating 7/9 done, changing it to 3/5 requires writing `Value` before `Maximum`, but setting it to 14/19 requires writing one must update `Maximum` first. A `SetFraction` method which accepted parameters for both value and maximum, and updated both simultaneously, would have been much better.
- Some properties like the time remaining on a timer may change between the time they're written and the time they're read. Such things may be perfectly fine as read-only properties (reading them would take time, and the act of taking time might affect future readings, but such side effect would be no different from any other code the system might execute) but they would violate the rule that a clean property should return the last value read.
If any of those conditions (or any of many others--the list is hardly exhaustive) apply, use a
SetXX method rather than a read-write property.