views:

221

answers:

6

I have a object with properties that are expensive to compute, so they are only calculated on first access and then cached.

 private List<Note> notes;
 public List<Note> Notes
    {
        get
        {
            if (this.notes == null)
            {
                this.notes = CalcNotes();
            }
            return this.notes;
        }
    }

I wonder, is there a better way to do this? Is it somehow possible to create a Cached Property or something like that in C#?

+2  A: 

Looks pretty standard to me. What you are doing is fine.

Oded
+1  A: 

Yes it is possible. The question is, how much you are winning by doing it - you still need the initialization code somewhere, so at most you will be saving the conditional expression.

A while ago I implemented a class to handle this. You can find the code posted in this question, where I ask whether it's a good idea. There are some interesting opinions in the answers, be sure to read them all before deciding to use it.

Edit:

A Lazy<T> class that does basically the same as my implementation that I link to above, has been added to the .NET 4 Framework; so you can use that if you are on .NET 4. See an example here: http://weblogs.asp.net/gunnarpeipman/archive/2009/05/19/net-framework-4-0-using-system-lazy-lt-t-gt.aspx

driis
+8  A: 

As far as syntax goes, you can use the null-coalescing operator if you want to be fancy, but it's not necessarily as readable.

get
{
    return notes ?? (notes = CalcNotes());
}

Edit: Updated courtesy of Matthew. Also, I think the other answers are more helpful to the question asker!

frou
`return notes ?? (notes = CalcNotes());` would be slightly more efficient.
Matthew Whited
how is this not as readable?
Jason w
Sorry I messed up the second `??`... I corrected it to `=`. This version will only do the secondary assignment if the value is null. Your version will always reassign the value.
Matthew Whited
@Matthew that is exactly how I handle any lazy properties as of current day.
Chris Marisic
Looks really cool! Will definitely be using this syntax for <=3.5.Net
Peterdk
A: 

If the value is non-trivial to compute, I generally prefer using a method (GetNotes()). There's nothing stopping you from caching the value with a method, plus you can add the [Pure] attribute (.NET 4) if applicable to indicate the method does not alter the state of the object.

If you do decide to stay with the following, I recommend:

Whenever you have a lazily-evaluated property, you should add the following attribute to ensure that running in the debugger behaves the same as running outside of it:

[DebuggerBrowsable(DebuggerBrowsableState.Never)]

Also, starting with .NET 4, you can use the following:

// the actual assignment will go in the constructor.
private readonly Lazy<List<Note>> _notes = new Lazy<List<Note>>(CalcNotes);

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public List<Note> Notes
{
    get { return _notes.Value; }
}
280Z28
Unless CalcNotes is static, this won't compile, since the inline construction requires a static context - you'll need to move the construction into the constructor...
Reed Copsey
@Reed Copsey: See the comment...?
280Z28
Ahh, okay - missed that ;) Misleading to have it defined inline, IMO.
Reed Copsey
+7  A: 

In .NET 3.5 or earlier, what you have is a very standard practice, and a fine model.

(Although, I would suggest returning IList<T>, or IEnumerable<T> if possible, instead of List<T> in your public API - List<T> should be an implementation detail...)

In .NET 4, however, there is a simpler option here: Lazy<T>. This lets you do:

private Lazy<IList<Note>> notes;
public IEnumerable<Note> Notes
{
    get
    {
        return this.notes.Value;
    }
}

// In constructor:
this.notes = new Lazy<IList<Note>>(this.CalcNotes);
Reed Copsey
Looks interesting, too bad you still need a backing variable. The amount of code is even more spread throughout the class, and it's not really less code to write, so no real benefits I would say. There should be a Lazy Property construction way, just like {get; private set;} but then with a reference to the calculation method that needs to be called.
Peterdk
Peterdk: THat's basically what `Lazy<T>` gives you.. it's tough to do "lazy construction" via a func<T> without having a separate way to define the construction function. It's quite a bit simpler without that (since you can just use "private Lazy<List<Note>> note = new Lazy<List<Note>>();" (no constructor issues)... This, however, does have the MAJOR advantage of being completely thread safe, where your original is not...
Reed Copsey
Reed: Ok, nice to know. Will look a bit more into the Lazy thing. Can't find out yet if it's supported on WP7, for now I'll use the null-coalescing operating suggested by frou. This is however definitely the way to go in future .Net versions.
Peterdk
A: 

The problem with ?? is that if CalcNotes() returns null then it will not be cached any more. Ditto for value types if for example both 0 and NaN are allowed as property value.

Much better would be an "aspect-oriented" solution, something like Post-Sharp does using attributes and then modifying MSIL (bytecode).

The code would look like:

[Cached]
public List<Note> Notes { get { return CalcNotes(); } }

EDIT: CciSharp.LazyProperty.dll does exactly this!!!

Mikhail