An interface I find very useful lately is IComputeOnDemand (although it is highly debatable if it should be an interface at all): /// <summary> /// Behavior for things that can be computed on demand. /// </summary> public interface IComputeOnDemand { /// <summary> /// Gets a value indicating whether this instance is computed. /// </summary> /// <value> /// <c>true</c> if this instance is computed; otherwise, <c>false</c>. /// </value> bool IsComputed { get; } /// <summary> /// Gets or sets the inner value. /// </summary> /// <value></value> object InnerValue { get; set; } } Classes often have properties that only need to be computed once. The most used approach to handle this is by introducing some variable (e.g. loaded, calculated) which indicates if some computation has taken place. An example: public class SomeClass { private bool loadedField1; private int field1; public int Field1 { get { if (!this.loadedField1){ //Do some "heavy" computation here. this.field1 = resultOfComputation; this.loadedField1 = true; } return this.field1; } } } While this is fine for one field, consider this scenario for multiple fields in one class. Pretty soon the class gets cluttered with these "loaded" fields. But there's even bigger danger lurking: What if you bypass the property and start using the field without it being initialized? Will you ever know? Ofcourse you only get this within the class itself (I'm assuming you've banned the evil called public fields). Let's add a new exception: /// <summary> /// The exception that is thrown when something is not computed. /// </summary> public class NotComputedException: ApplicationException { /// <summary> /// Creates a new <see cref="NotComputedException"/> instance. /// </summary> public NotComputedException(): base() {} /// <summary> /// Creates a new <see cref="NotComputedException"/> instance. /// </summary> /// <param name="message">Message.</param> public NotComputedException(string message): base(message) {} /// <summary> /// Creates a new <see cref="NotComputedException"/> instance. /// </summary> /// <param name="message">Message.</param> /// <param name="innerException">Inner exception.</param> public NotComputedException(string message, Exception innerException): base(message, innerException) {} } What's next? Well, time for some code smitherings: For each of your compute on demand data types, you generate a "typed" holder class. I took System.Boolean as an example (mind the "no boxing here"): /// <summary> /// Represents a placeholder for a <see cref="Boolean"/> value. /// </summary> public class BooleanHolder: IComputeOnDemand { /// <summary> /// Creates a new <see cref="BooleanHolder"/> instance. /// </summary> public BooleanHolder() { } /// <summary> /// Creates a new <see cref="BooleanHolder"/> instance. /// </summary> /// <param name="innerValue">Inner value.</param> public BooleanHolder(Boolean innerValue){ this.InnerValue = innerValue; } /// <summary> /// Gets a value indicating whether this instance is computed. /// </summary> /// <value> /// <c>true</c> if this instance is computed; otherwise, <c>false</c>. /// </value> public bool IsComputed { get { return this._computed; } }bool _computed = false; /// <summary> /// Gets or sets the inner value. /// </summary> /// <value></value> public Boolean InnerValue{ get { if (!this.IsComputed) { throw new NotComputedException(); } return this._innerValue; } set { this._innerValue = value; this.MarkAsComputed(); } }Boolean _innerValue; /// <summary> /// Gets or sets the inner value. /// </summary> /// <value></value> object IComputeOnDemand.InnerValue { get { return this.InnerValue; } set { this.InnerValue = (Boolean)value; } } /// <summary> /// Marks as computed. /// </summary> /// <returns></returns> private void MarkAsComputed(){ this._computed = true; } } Using this approach you get a less cluttered class and a nice exception when using a field that wasn't computed. As noted by John Ritsema, in Whidbey generics can be used to simplify things (see comment for how-to). public class SomeClass { private BooleanHolder field1; public bool Field1 { get { if(!field1.IsComputed){ //Do some "heavy" computation here. this.field1.InnerValue = resultOfComputation; } return this.field1.InnerValue; } } } I tend to appreciate the small things in life...
|