Я не знаю, какие различия между LazyInitializer и Lazy <T> . Я знаю, что оба они будут инициализировать объект только по требованию. Когда мне нужно использовать каждый из них?
LazyInitializer vs Lazy <T>. Когда использовать каждый
Ответ 1
Lazy<T>
(MSDN) - это общая оболочка, которая позволяет создавать экземпляр T
по запросу, удерживая метод T
factory (Func<T>
) и вызывая его при доступе к ресурсу Vaue
.
LazyInitializer
- статический класс с набором статических методов, это просто помощник, который использует Activator.CreateInstance() (отражение), способный создать экземпляр заданного типа. Он не поддерживает локальные частные поля и не предоставляет никаких свойств, поэтому накладные расходы на использование памяти не будут.
Стоит отметить, что оба класса используют Func<T>
как экземпляр factory.
MSDN говорит несколько слов о LazyInitializer
классе:
Эти процедуры не требуют выделения выделенного, lazy-initialization instance, вместо этого используя ссылки для обеспечения цели были инициализированы по мере их доступа.
PS:
Я нашел интересный способ, как LazyIntiializer
проверяет, был ли экземпляр уже инициализирован, он просто сравнивает переданный в ссылке , nice:
private static T EnsureInitializedCore<T>(ref T target, Func<T> valueFactory)
where T : class
{
T t = valueFactory();
if (t == null)
{
throw new InvalidOperationException(Environment.GetResourceString("Lazy_StaticInit_InvalidOperation"));
}
Interlocked.CompareExchange<T>(ref target, t, default(T));
return target;
}
Что мне кажется странным, он создает новый экземпляр каждый раз перед фактической проверкой:
T t = valueFactory();
// ... and only then does check
Ответ 2
Я не уверен, что вы все еще смотрите на это, но мне пришлось подробно изучить детали как Lazy<T>
, так и LazyInitializer.EnsureInitialized<T>()
, поэтому я решил поделиться своими выводами.
Во-первых, некоторые числа. Я запускал тесты с использованием обоих методов в пакетах из десяти миллионов значений с использованием обоих подходов, тестирования использования памяти с помощью GC.GetTotalMemory(true)
и получения таймингов Stopwatch
для создания экземпляра, первого доступа к значениям и последующего доступа к значениям:
Lazy<T> Memory Use: 320,000,000 bytes (32B/instance)
EnsureInitialized<T>() Memory Use: N/A
Lazy<T> Instantiation Time: 622.01 ms
EnsureInitialized<T>() Inst. Time: N/A
Lazy<T> First Access: 1,373.50 ms
EnsureInitialized<T>() First Access: 72.94 ms
Lazy<T> Subsequent Accesses: 18.51 ms
EnsureInitialized<T>() Subsequent: 13.75 ms
(я использовал LazyThreadSafetyMode.PublicationOnly
с Lazy<T>'s
, который по-прежнему будет одним и тем же методом безопасности потока, принятым по умолчанию LazyInitializer
.)
Как вы можете видеть, если я каким-то образом не придумал свои тесты (никогда не исключаю!), в этих условиях LazyInitializer
превосходит почти каждый измеримый способ. Он не имеет накладных расходов памяти или экземпляра, и он быстрее работает как для создания, так и для получения значения.
Итак, зачем вы хотите использовать Lazy<T>
? Во-первых, это были результаты теста на моей системе x64, и, возможно, вы можете получить разные результаты при других обстоятельствах.
Lazy<T>
также может привести к более четкому и более сжатому коду. return myLazy.Value;
намного удобнее, чем return LazyInitializer.EnsureInitialized(ref myValue, () => GetValue(foo));
Кроме того, Lazy<T>
делает вещи намного проще, если вы имеете дело со типом значений или ссылочным типом, который может быть законным null
. С помощью LazyInitializer
вам нужно использовать второе логическое поле, чтобы отслеживать, было ли инициализировано значение, что упрощает проблему четкости кода. Lazy<T>
также проще использовать, если вы хотите более жесткую безопасность потоков.
И в великой схеме вещей большая часть накладных расходов, вероятно, незначительна для многих приложений (хотя не всегда - причина, по которой я начал изучать это, потому что я работал над приложением, в котором миллионы были очень ленивыми загруженные значения, а накладные расходы на 32 байта на экземпляр Lazy<T>
фактически стали неудобными).
В конце концов, если ваше приложение не очень интенсивно для памяти, я думаю, что это обычно будет вопрос личных предпочтений. Для ненулевых ссылочных типов я лично считаю, что LazyInitializer.EnsureInitialized<T>()
- более элегантный подход, но я также могу выкопать аргумент ясности кода.
Ответ 3
Как говорят другие ответы,
Lazy<T>
-
Обычно дает более чистый код: просто инициализируйте с помощью
x = new Lazy<T>(_ => new ...)
и используйтеx.Value
везде, где вы его открываете. -
Позволяет использовать разные предопределенные параметры для обработки инициализации и исключений, если несколько потоков обращаются к свойству
Value
неинициализированного объектаLazy<T>
одновременно.
LazyInitializer
-
Сохраняет пробел и, возможно, время: нет необходимости инициализировать новый объект
Lazy<T>
для каждой переменной, которую вы объявляете. -
Позволяет задерживать параметры инициализации до времени использования:
LazyInitializer.EnsureInitialized(ref x, () => new X(initParameters))
В заключении вам нужно использовать LazyInitializer
, если ограниченное пространство (и, возможно, время), или если вы не можете указать все параметры инициализации во время объявления.
Лично я предпочитаю Lazy<T>
, когда это возможно, потому что я нахожу, что он дает более чистый код, и мне не нужно явно обрабатывать исключения инициализации.
Ответ 4
LazyInitializer
позволяет вам выполнять ленивую инициализацию без накладных расходов на создание класса для каждого лениво инициализированного объекта.
Здесь могут быть предоставлены преимущества LazyInitializer
.
Это зависит от ваших собственных требований относительно того, слишком ли сложны служебные данные, созданные с помощью Lazy<T>
.
Ответ 5
Документация по Lazy Initialization объясняет это довольно четко. См. Lazy Initialization. Короче говоря, Lazy<T>
создает новый класс (построенный общий) для каждого используемого T
и новый экземпляр этого класса для каждого экземпляра T
, который вы декальр, даже если базовый T
никогда не будет инициализируется. Использование статических методов LazyIntializer
может быть более сложным для кодирования, но позволяет избежать накладных расходов экземпляров оболочки Lazy<T>
.
Ответ 6
Я думаю, это ответ на ваш вопрос: Другой способ LazyInitialization System.Threading.ThreadLocal
Это то же самое, что и Lazy, но единственное отличие состоит в том, что он хранит данные на основе Thread Local. Таким образом, значения для каждого потока были бы другой копией инициализированного объекта.
более подробная информация: http://www.cshandler.com/2011/09/different-ways-of-lazy-initialization.html
Ответ 7
`LazyInitializer` of an object means its object creation is deferred until it is ued first.
Объект этой формы создан для повышения производительности и уменьшения потерь памяти.
В то время как для определения типа Lazy-initialized мы используем Lazy<T>
(общая форма) класса LazyInitializer
E.g:
Lazy<Orders> _orders = new Lazy<Orders>();
Для дальнейших ссылок: