Подтвердить что ты не робот

Кэшированная собственность: проще?

У меня есть объект со свойствами, которые дорого вычисляются, поэтому они вычисляются только при первом доступе и затем кэшируются.

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

Интересно, есть ли лучший способ сделать это? Возможно ли создать Cached-свойство или что-то подобное в С#?

4b9b3361

Ответ 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; }
}