Кэш-память vs Lazy <T>
В .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; }
}
}
Ответы
Ответ 1
Я бы использовал Lazy<T>
в целом:
- Это поточно-безопасный (может быть, не проблема в этом случае, но будет в других)
- Это делает очевидным, что происходит только по имени
- Он позволяет null быть допустимым значением
Обратите внимание, что вам не нужно использовать выражение лямбда для делегата. Например, здесь подход, который может быть немного чище:
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>
не только для чтения), но я очень редко обнаружил, что это важно.
Ответ 2
Используйте Lazy<T>
, поскольку он точно выражает то, что вы делаете - ленивая загрузка.
Кроме того, он сохраняет ваше свойство очень чистым и безопасным потоком.
Ответ 3
Как правило, единственная причина, по которой не использовать ленивый, - это reset переменная, равная null, поэтому следующий доступ заставляет ее снова загружаться. Lazy не имеет reset, и вам нужно будет воссоздать ленивый с нуля.
Ответ 4
Lazy<T>
будет правильно переносить параллельные сценарии (если вы передадите правильный LazyThreadSafetyMode), в то время как ваш пример не имеет проверок безопасности потоков.
Ответ 5
Lazy<T>
проще, он четко выражает намерение кода.
Это также безопасно для потоков.
Обратите внимание: если вы на самом деле используете это для нескольких потоков, вам нужно сделать его [ThreadStatic]
; Объекты GDI + не могут быть разделены между потоками.
Ответ 6
У Lazy есть некоторые накладные расходы на синхронизацию, чтобы обеспечить безопасность потоков, тогда как свойство кеширования инициализируется способом CLR перед любым другим кодом, и вам не нужно оплачивать затраты на синхронизацию
С точки зрения проверки, Lazy хорошо протестирован и проверен артефактом.
Однако, на мой взгляд, он имеет очень небольшие накладные расходы по сравнению с другим вариантом
Ответ 7
Хорошо, если ваша производительность примерно такая же, единственная причина использовать Lazy<T>
над кешированной версией будет, если вы не уверены, действительно ли пользователь загрузит свойство.
Точкой Lazy<T>
является ожидание, пока пользователь не потребует ресурс, а затем создаст его в этом экземпляре во времени. Если им всегда нужен ресурс, тогда нет смысла использовать Lazy<T>
, если вам не нужны некоторые другие цели, такие как потокобезопасность.