Что такое ленивая инициализация и чем она полезна?

83

Что такое ленивая инициализация объектов? Как вы это делаете и каковы преимущества?

СНС
источник

Ответы:

105

Ленивая инициализация - это оптимизация производительности, при которой вы откладываете (потенциально дорогое) создание объекта до того момента, когда он вам действительно понадобится.

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

Основная причина этого заключается в том, что (часто) вы можете полностью избежать создания объекта, если он вам никогда не понадобится.

Беван
источник
39
Это должно быть само собой разумеющимся, но на всякий случай, если он это пропустил: причина, по которой это оптимизация, в том, что вы часто обнаруживаете, что вам вообще не нужно делать создание, и потому что вы можете сэкономить, выполняя некоторую работу, когда компьютер уже занят. до тех пор, пока он не станет менее занятым.
Joel Coehoorn
Хороший комментарий. Я изменил ответ, чтобы учесть вашу точку зрения.
Беван,
очень хороший (и другой) вариант использования / причина использования Lazyопубликован в следующем вопросе SO: stackoverflow.com/a/15894928/4404962 это предложение решило именно то, что нам нужно было решить - используя Lazyв качестве ключевого принципала
lev haikin
51

Как уже упоминалось, ленивая инициализация откладывает инициализацию до тех пор, пока не будет использован компонент или объект. Вы можете рассматривать ленивую инициализацию как приложение времени выполнения по принципу YAGNI - " You ain't gonna need it"

Преимущества отложенной инициализации с точки зрения приложения состоят в том, что пользователям не нужно тратить время на инициализацию для функций, которые они не будут использовать. Предположим, вы должны инициализировать каждый компонент своего приложения заранее. Это может создать потенциально долгое время запуска - пользователям придется ждать десятки секунд или минут, прежде чем ваше приложение будет готово к использованию. Они ждут и платят за инициализацию функций, которые могут никогда не использовать или не использовать сразу.

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

Майкл
источник
Жаль, что я мог дать только +1! Хорошее обоснование, кстати.
Rajendra Uppal
1
YAGNI означает «вам не понадобится [функция, код]». Ленивая инициализация - это оптимизация, которая сама по себе является функцией. Я бы сказал, что это применяется наоборот: если вы точно не знаете, что вам нужна ленивая инициализация, не используйте это, YAGNI.
Флавий
19

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

Лично я использовал отложенную инициализацию при создании собственной ORM, созданной вручную в .NET 2.0. При загрузке моих коллекций из базы данных фактические элементы в коллекции инициализировались отложенным образом. Это означало, что коллекции создавались быстро, но каждый объект загружался только тогда, когда мне это требовалось.

Если вы знакомы с шаблоном Singleton, вы, вероятно, также видели в действии ленивую инициализацию.

public class SomeClassSingleton
{
    private static SomeClass _instance = null;

    private SomeClassSingleton()
    {
    }

    public static SomeClass GetInstance()
    {
        if(_instance == null)
            _instance = new SomeClassSingleton();

        return _instance;
    }
}

В этом случае экземпляр SomeClass не инициализируется до тех пор, пока он не понадобится потребителю SomeClassSingleton.

Джастин Нисснер
источник
Удалите явную настройку частной переменной на «null», чтобы немного улучшить скорость;). Во всяком случае, это лишнее.
nicodemus13
хороший момент, SomeClassSingleton должен быть равным или более низким классом SomeClass
Фредрик Гаусс
Я не знаю, читает ли кто-нибудь старые ответы, но я предполагаю, что метод экземпляра private static get должен быть общедоступным?
Biscuit128,
@ Biscuit128 - Я их читаю, и да, так и должно быть. Я обновил его на случай, если кто-нибудь еще это прочитает.
Джастин Нисснер
не должно _instance = new SomeClassSingleton (); быть _instance = new SomeClass (); ? или я понимаю с другой точки зрения
ashishdhiman2007
6

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

Простым примером этого является System.Exception.StackTrace. Это строковое свойство исключения, но на самом деле оно не создается, пока вы не получите к нему доступ. Внутри он делает что-то вроде:

String StackTrace{
  get{
    if(_stackTrace==null){
      _stackTrace = buildStackTrace();
    }
    return _stackTrace;
  }
}

Это избавит вас от накладных расходов на вызов buildStackTrace до тех пор, пока кто-то не захочет узнать, что это такое.

Свойства - это один из способов просто обеспечить такое поведение.

Дельфин
источник
2
Верно, но я не уверен, почему вы все равно делитесь ссылкой на исключение между несколькими потоками.
Дельфин
3

Здесь вы можете прочитать о ленивой инициализации с примером кода.

  • Когда у вас есть объект, который дорого создавать, и программа может его не использовать. Например, предположим, что у вас есть в памяти объект Customer, у которого есть свойство Orders, содержащее большой массив объектов Order, для инициализации которых требуется соединение с базой данных. Если пользователь никогда не просит отображать Заказы или использовать данные в вычислениях, то нет причин использовать системную память или вычислительные циклы для их создания. Используя Lazy для объявления объекта Orders для отложенной инициализации, вы можете избежать траты системных ресурсов, когда объект не используется.

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

Амир Резаи
источник
3

Ленивая инициализация объекта означает, что его создание откладывается до первого использования. (В этом разделе термины «ленивая инициализация» и «ленивое создание экземпляров» являются синонимами.) Ленивая инициализация в основном используется для повышения производительности, предотвращения бесполезных вычислений и уменьшения требований к памяти программы. Это наиболее распространенные сценарии:

Когда у вас есть объект, который дорого создавать, и программа может его не использовать. Например, предположим, что у вас есть в памяти объект Customer, у которого есть свойство Orders, содержащее большой массив объектов Order, для инициализации которых требуется соединение с базой данных. Если пользователь никогда не просит отображать Заказы или использовать данные в вычислениях, то нет причин использовать системную память или вычислительные циклы для их создания. Используя Lazy для объявления объекта Orders для отложенной инициализации, вы можете избежать траты системных ресурсов, когда объект не используется.

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

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

Ракеш Кумар
источник
2
//Lazy instantiation delays certain tasks. 
//It typically improves the startup time of a C# application.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LazyLoad
{
    class Program
    {
        static void Main(string[] args)
        {
            Lazy<MyClass> MyLazyClass = new Lazy<MyClass>(); // create lazy class
            Console.WriteLine("IsValueCreated = {0}",MyLazyClass.IsValueCreated); // print value to check if initialization is over

            MyClass sample = MyLazyClass.Value; // real value Creation Time
            Console.WriteLine("Length = {0}", sample.Length); // print array length

            Console.WriteLine("IsValueCreated = {0}", MyLazyClass.IsValueCreated); // print value to check if initialization is over
            Console.ReadLine();
        }
    }

    class MyClass
    {
        int[] array;
        public MyClass()
        {
            array = new int[10];

        }

        public int Length
        {
            get
            {
                return this.array.Length;
            }
        }
    }
}


// out put

// IsValueCreated = False
// Length = 10
// IsValueCreated = True
Картик Кришна Байджу
источник
1

Что я понял о ленивой инициализации, так это то, что программа не загружает / не запрашивает все данные за один раз. Он ожидает его использования, прежде чем запрашивать его, например. SQL-сервер.

Если у вас есть база данных с большой таблицей, объединенной с большим количеством подтаблиц, и вам не требуются детали, соединенные с другими таблицами, если вы не входите в «редактировать» или «просматривать детали», тогда Lazy Init. поможет приложению работать быстрее и сначала «лениво» загружать данные при необходимости.

В SQL или LINQ вы можете установить этот «параметр» в вашей модели базы данных pr. dataelement.

Надеюсь, в этом есть смысл для вашего вопроса?

Веб-клиент (например, браузер) делает то же самое. Изображения "лениво загружаются" после HTML, и AJAX также является "разновидностью ленивой инициализации", если используется правильно.

BerggreenDK
источник
1

Примеры баз данных, которые были упомянуты до сих пор, хороши, но они не ограничиваются только уровнем доступа к данным. Вы можете применить те же принципы к любой ситуации, когда производительность или память могут быть проблемой. Хороший пример (хотя и не .NET) - в Какао, где вы можете подождать, пока пользователь не запросит окно, чтобы фактически загрузить его (и связанные с ним объекты) из пера. Это может помочь снизить использование памяти и ускорить начальную загрузку приложения, особенно когда вы говорите о таких вещах, как окна настроек, которые не понадобятся до некоторого времени, если вообще когда-либо.

Марк Шарбонно
источник