Кэшированная собственность: проще?
У меня есть объект со свойствами, которые дорого вычисляются, поэтому они вычисляются только при первом доступе и затем кэшируются.
private List<Note> notes;
public List<Note> Notes
{
get
{
if (this.notes == null)
{
this.notes = CalcNotes();
}
return this.notes;
}
}
Интересно, есть ли лучший способ сделать это? Возможно ли создать Cached-свойство или что-то подобное в С#?
Ответы
Ответ 1
Что касается синтаксиса, вы можете использовать оператор null-coalescing, если вы хотите быть фантастическим, но это не обязательно как читаемый.
get
{
return notes ?? (notes = CalcNotes());
}
Изменить: Обновлена любезность Мэтью. Кроме того, я думаю, что другие ответы более полезны для вопросника!
Ответ 2
В .NET 3.5 или ранее у вас есть очень стандартная практика и прекрасная модель.
(Хотя я бы предложил вернуться IList<T>
или IEnumerable<T>
, если это возможно, вместо List<T>
в вашем общедоступном API - List<T>
должна быть детальностью реализации...)
В .NET 4, однако, здесь есть более простой вариант: Lazy<T>
. Это позволяет:
private Lazy<IList<Note>> notes;
public IEnumerable<Note> Notes
{
get
{
return this.notes.Value;
}
}
// In constructor:
this.notes = new Lazy<IList<Note>>(this.CalcNotes);
Ответ 3
Проблема с ??
заключается в том, что если CalcNotes()
возвращает null
, то он больше не будет кэшироваться. То же самое относится к типам значений, если в качестве значения свойства допускаются как 0
, так и NaN
.
Гораздо лучше было бы "аспектно-ориентированное" решение, что-то вроде Post - Sharp использует атрибуты, а затем модифицирует MSIL (байт-код).
Код будет выглядеть так:
[Cached]
public List<Note> Notes { get { return CalcNotes(); } }
EDIT: CciSharp.LazyProperty.dll делает именно это!!!
Ответ 4
Выглядит довольно стандартно для меня. То, что вы делаете, прекрасно.
Ответ 5
Да возможно. Вопрос в том, насколько вы выигрываете, делая это - вам все еще нужен код инициализации, поэтому в большинстве случаев вы будете сохранять условное выражение.
Недавно я реализовал класс для обработки этого. Вы можете найти код, отправленный в этот вопрос, где я спрашиваю, хорошая ли это идея. В ответах есть несколько интересных мнений, обязательно прочитайте их все, прежде чем принимать решение об использовании.
Изменить:
A Lazy <T> класс, который в основном совпадает с моей реализацией, с которой я ссылаюсь выше, был добавлен в .NET.NET Framework; поэтому вы можете использовать это, если вы находитесь на .NET 4. См. пример здесь: http://weblogs.asp.net/gunnarpeipman/archive/2009/05/19/net-framework-4-0-using-system-lazy-lt-t-gt.aspx
Ответ 6
Если значение нетривиально для вычисления, я обычно предпочитаю использовать метод (GetNotes()
). Там ничего не мешает вам кэшировать значение с помощью метода, плюс вы можете добавить атрибут [Pure]
(.NET 4), если это применимо, чтобы указать метод не изменяет состояние объекта.
Если вы решите остаться со следующим, я рекомендую:
Всякий раз, когда у вас есть свойство с ленивой оценкой, вы должны добавить следующий атрибут, чтобы убедиться, что работа в отладчике ведет себя так же, как работа вне его:
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Также, начиная с .NET 4, вы можете использовать следующее:
// 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; }
}