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

LazyInitializer vs Lazy <T>. Когда использовать каждый

Я не знаю, какие различия между LazyInitializer и Lazy <T> . Я знаю, что оба они будут инициализировать объект только по требованию. Когда мне нужно использовать каждый из них?

4b9b3361

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

Для дальнейших ссылок:

http://msdn.microsoft.com/en-us/library/dd997286.aspx