Yves Reynhout's Blog
The seagile man
Computing on demand
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...
Comments
#
John
said on 02 June, 2005 03:51 PM
Or in Whidbey we could do something like:
public class Holder<T> {
private T _innerValue = default(T);
private bool _computed = false;
public Holder(T innerValue) {
this.InnerValue = innerValue;
}
public bool IsComputed {
get {
return this._computed;
}
}
public T InnerValue {
get {
if (!this.IsComputed) {
throw new NotComputedException();
}
return this._innerValue;
}
set {
this._innerValue = value;
this.MarkAsComputed();
}
}
private void MarkAsComputed() {
this._computed = true;
}
}
#
John
said on 02 June, 2005 03:54 PM
And then...
class TestHolderClass {
private Holder<bool> _field1 = new Holder<bool>(false);
public bool Field1 {
get {
if (!_field1.IsComputed) {
this._field1.InnerValue = true;
}
return this._field1.InnerValue;
}
}
}
Leave a Comment
Title
(required)
Name
(required)
Your URL
(optional
)
Comments
(required)
Remember Me?
Search
Go
This Blog
Home
About
Tags
.NET
Code generation
Corporate
Mediator
MVVM
NAuthorize
Object/Relational Mapping
Silverlight
SOA
WPF
Navigation
Home
Blogs
Archives
October 2009 (1)
September 2005 (1)
June 2005 (1)
May 2005 (1)
April 2005 (5)
October 2004 (1)
June 2004 (3)
March 2004 (2)
February 2004 (1)
January 2004 (1)
December 2003 (1)
November 2003 (1)
October 2003 (8)
July 2003 (1)
May 2003 (1)
April 2003 (2)
March 2003 (3)
February 2003 (5)
NAuthorize
NAuthorize on forge.novell.com
Sun's XACML implementation
XACML @ OASIS
RBAC @ NIST
Ravi S. Sandhu
RAD by Healthcare DTF @ OMG
Testdriven.net
Syndication
RSS
Atom
Comments RSS