Reputation: 16005
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#?
Upvotes: 52
Views: 20505
Reputation: 7553
A two-line option with C# 8 and null-coalescing assignment:
private List<Note>? notes;
public List<Note> Notes => notes ??= CalcNotes();
Upvotes: 8
Reputation: 27827
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!
Upvotes: 37
Reputation: 5831
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!!!
Upvotes: 6
Reputation: 564333
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);
Upvotes: 23
Reputation: 164281
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.
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
Upvotes: 2
Reputation: 99859
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; }
}
Upvotes: 1