Кэшированное свойство против Lazy <T> - PullRequest
50 голосов
/ 27 февраля 2011

В .NET 4 следующий фрагмент с кэшированным свойством также может быть записан с использованием класса System.Lazy<T>. Я измерил производительность обоих подходов, и она почти одинакова. Есть ли какая-то реальная польза или магия, почему я должен использовать один над другим?

Кэшированное свойство

public static class Brushes
{
    private static LinearGradientBrush _myBrush;

    public static LinearGradientBrush MyBrush
    {
        get
        {
            if (_myBrush == null)
            {
                var linearGradientBrush = new LinearGradientBrush { ...};
                linearGradientBrush.GradientStops.Add( ... );
                linearGradientBrush.GradientStops.Add( ... );

                _myBrush = linearGradientBrush;
            }

            return _myBrush;
        }
    }
}

Ленивый

public static class Brushes
{
    private static readonly Lazy<LinearGradientBrush> _myBrush =
        new Lazy<LinearGradientBrush>(() =>
            {
                var linearGradientBrush = new LinearGradientBrush { ...};
                linearGradientBrush.GradientStops.Add( ... );
                linearGradientBrush.GradientStops.Add( ... );

                return linearGradientBrush;
            }
        );

    public static LinearGradientBrush MyBrush
    {
        get { return _myBrush.Value; }
    }
}

Ответы [ 7 ]

71 голосов
/ 27 февраля 2011

Я бы использовал Lazy<T> в целом:

  • Это поточно-ориентированный (может не быть проблемой в этом случае, но будет в других)
  • Это делает егоочевидно, что происходит только по имени
  • . Это позволяет пустому значению быть допустимым

Обратите внимание, что у вас нет для использования лямбда-выражения дляделегат.Например, вот подход, который может быть немного чище:

public static class Brushes
{
    private static readonly Lazy<LinearGradientBrush> _myBrush =
        new Lazy<LinearGradientBrush>(CreateMyBrush);

    private static LinearGradientBrush CreateMyBrush()
    {
        var linearGradientBrush = new LinearGradientBrush { ...};
        linearGradientBrush.GradientStops.Add( ... );
        linearGradientBrush.GradientStops.Add( ... );

        return linearGradientBrush;
    }

    public static LinearGradientBrush MyBrush
    {
        get { return _myBrush.Value; }
    }
}

Это особенно удобно, когда процесс создания усложняется циклами и т. Д. Обратите внимание, что по внешнему виду вы можете использовать инициализатор коллекции дляGradientStops в вашем коде создания.

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

Как отмечается в ответе DoubleDown, нет способа сбросить его для принудительного пересчета (если вы не сделаете поле Lazy<T> не только для чтения) - но я очень редко обнаруживал, что это важно.

7 голосов
/ 27 февраля 2011

Используйте Lazy<T>, поскольку оно точно отражает то, что вы делаете - ленивая загрузка.

Кроме того, он сохраняет вашу собственность в чистоте и безопасен для нитей.

4 голосов
/ 27 февраля 2011

Обычно единственная причина не использовать lazy - сбросить переменную в null, чтобы при следующем доступе она снова загружалась. Ленивый не имеет сброса, и вам нужно будет воссоздать ленивый с нуля.

2 голосов
/ 27 февраля 2011

Lazy<T> будет правильно обрабатывать параллельные сценарии (если вы передадите правильный LazyThreadSafetyMode ), в то время как в вашем примере нет никаких проверок безопасности потока.

1 голос
/ 27 февраля 2011

Lazy<T> проще - он четко выражает намерение кода.
Это также потокобезопасно.

Обратите внимание, что если вы на самом деле используете это в нескольких потоках, вам нужно сделатьэто [ThreadStatic];Объекты GDI + не могут быть общими для всех потоков.

0 голосов
/ 07 октября 2011

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

С точки зрения тестируемости, Lazy - это хорошо проверенный и проверенный артефакт.

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

0 голосов
/ 27 февраля 2011

Что ж, если ваша производительность примерно одинакова, единственной причиной использования Lazy<T> над кэшированной версией будет отсутствие уверенности в том, что пользователь действительно собирается загрузить свойство.

Смысл Lazy<T> состоит в том, чтобы подождать, пока пользователю понадобится ресурс, а затем создать его в тот момент времени. Если они всегда будут нуждаться в ресурсах, то нет смысла использовать Lazy<T>, если вам не нужны какие-то другие его цели, такие как поточнобезопасность.

...