Was ist eine verzögerte Initialisierung und warum ist sie nützlich?

83

Was ist eine verzögerte Initialisierung von Objekten? Wie machst du das und was sind die Vorteile?

SNA
quelle

Antworten:

105

Lazy Initialization ist eine Leistungsoptimierung, bei der Sie die (möglicherweise teure) Objekterstellung verschieben, bis Sie sie tatsächlich benötigen.

Ein gutes Beispiel ist, keine Datenbankverbindung im Voraus zu erstellen, sondern nur kurz bevor Sie Daten aus der Datenbank abrufen müssen.

Der Hauptgrund dafür ist, dass Sie (häufig) vermeiden können, das Objekt vollständig zu erstellen, wenn Sie es nie benötigen.

Bevan
quelle
39
Es sollte selbstverständlich sein, aber nur für den Fall, dass er es verpasst hat: Der Grund für die Optimierung ist, dass Sie häufig feststellen, dass Sie die Erstellung überhaupt nicht durchführen müssen, und dass Sie möglicherweise die Arbeit sparen, wenn der Computer bereits ausgelastet ist bis zu einer Zeit, in der es weniger beschäftigt ist.
Joel Coehoorn
Guter Kommentar. Ich habe die Antwort geändert, um Ihren Standpunkt aufzunehmen.
Bevan
Ein sehr guter (und anderer) Anwendungsfall / Grund für die Verwendung Lazyist in der folgenden SO-Frage aufgeführt: stackoverflow.com/a/15894928/4404962 Dieser Vorschlag löste genau das, was wir lösen mussten - Lazyals
Hauptprinzip
50

Wie andere bereits erwähnt haben, verzögert die verzögerte Initialisierung die Initialisierung, bis eine Komponente oder ein Objekt verwendet wird. Sie können die verzögerte Initialisierung als Laufzeitanwendung des YAGNI-Prinzips anzeigen - " You ain't gonna need it"

Die Vorteile einer verzögerten Initialisierung aus Anwendungssicht bestehen darin, dass Benutzer die Initialisierungszeit nicht für Funktionen bezahlen müssen, die sie nicht verwenden. Angenommen, Sie würden jede Komponente Ihrer Anwendung im Voraus initialisieren. Dies kann zu einer möglicherweise langen Startzeit führen. Benutzer müssen Dutzende von Sekunden oder Minuten warten, bis Ihre Anwendung einsatzbereit ist. Sie warten auf die Initialisierung von Funktionen, die sie möglicherweise nie oder nicht sofort nutzen.

Wenn Sie die Initialisierung dieser Komponenten bis zur Nutzungsdauer verschieben, wird Ihre Anwendung stattdessen viel schneller gestartet. Der Benutzer muss weiterhin die Startkosten bezahlen, wenn er andere Komponenten verwendet. Diese Kosten werden jedoch über die Programmlaufzeit amortisiert und nicht auf den Anfang reduziert, und der Benutzer kann die Initialisierungszeit dieser Objekte mit den Funktionen verknüpfen, die sie sind mit.

Michael
quelle
Schade, dass ich nur +1 geben konnte! Gute Begründung, übrigens.
Rajendra Uppal
1
YAGNI bedeutet "du wirst es nicht brauchen [die Funktion, den Code]". Die verzögerte Initialisierung ist eine Optimierung, die ein Merkmal für sich ist. Ich würde eher sagen, dass es umgekehrt gilt: Wenn Sie nicht sicher sind, dass Sie eine verzögerte Initialisierung benötigen, verwenden Sie es nicht, YAGNI.
Flavius
18

Lazy Initialization ist das Konzept, die Objekterstellung zu verschieben, bis das Objekt tatsächlich zum ersten Mal verwendet wird. Bei sachgemäßer Verwendung kann dies zu erheblichen Leistungssteigerungen führen.

Persönlich habe ich Lazy Initialization verwendet, um mein eigenes handgerolltes ORM in .NET 2.0 zu erstellen. Beim Laden meiner Sammlungen aus der Datenbank wurden die tatsächlichen Elemente in der Sammlung verzögert initialisiert. Dies bedeutete, dass die Sammlungen schnell erstellt wurden, aber jedes Objekt nur geladen wurde, wenn ich es benötigte.

Wenn Sie mit dem Singleton-Muster vertraut sind, haben Sie wahrscheinlich auch eine verzögerte Initialisierung in Aktion gesehen.

public class SomeClassSingleton
{
    private static SomeClass _instance = null;

    private SomeClassSingleton()
    {
    }

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

        return _instance;
    }
}

In diesem Fall wird die Instanz von SomeClass erst initialisiert, wenn sie vom SomeClassSingleton-Consumer zum ersten Mal benötigt wird.

Justin Niessner
quelle
Entfernen Sie die explizite Einstellung für private Variablen auf 'null', um die Geschwindigkeit etwas weiter zu verbessern;). Wie auch immer, es ist überflüssig.
nicodemus13
schöner Punkt, SomeClassSingleton muss gleich oder niedriger Klasse von SomeClass sein
Fredrick Gauss
Ich weiß nicht, ob jemand alte Antworten liest, aber ich gehe davon aus, dass die private statische get-Instanzmethode öffentlich sein sollte.
Biscuit128
@ Biscuit128 - Ich lese sie und ja, das sollte es sein. Ich habe es aktualisiert, nur für den Fall, dass noch jemand dies liest.
Justin Niessner
sollte nicht _instance = new SomeClassSingleton (); be _instance = new SomeClass (); ? oder ich verstehe aus einer anderen Perspektive
ashishdhiman2007
6

Im Allgemeinen bedeutet "verzögerte Auswertung", die Verarbeitung auf etwas zu verschieben, bis Sie es tatsächlich benötigen. Die Hauptidee ist, dass Sie manchmal kostspielige Vorgänge vermeiden können, wenn Sie feststellen, dass Sie sie nicht benötigen oder wenn sich der Wert ändern würde, bevor Sie ihn verwenden.

Ein einfaches Beispiel hierfür ist System.Exception.StackTrace. Dies ist eine Zeichenfolgeeigenschaft für eine Ausnahme, die jedoch erst erstellt wird, wenn Sie darauf zugreifen. Intern macht es so etwas wie:

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

Dies erspart Ihnen den Aufwand, buildStackTrace tatsächlich aufzurufen, bis jemand sehen möchte, was es ist.

Eigenschaften sind eine Möglichkeit, diese Art von Verhalten einfach bereitzustellen.

Delfin
quelle
2
Stimmt, aber ich bin mir nicht sicher, warum Sie ohnehin einen Verweis auf eine Ausnahme zwischen mehreren Threads teilen würden.
Delphin
3

Hier können Sie über Lazy Initialization mit Beispielcode lesen .

  • Wenn Sie ein Objekt haben, dessen Erstellung teuer ist und das Programm es möglicherweise nicht verwendet. Angenommen, Sie haben ein Kundenobjekt im Speicher, das über eine Orders-Eigenschaft verfügt, die ein großes Array von Order-Objekten enthält, für deren Initialisierung eine Datenbankverbindung erforderlich ist. Wenn der Benutzer niemals auffordert, die Bestellungen anzuzeigen oder die Daten für eine Berechnung zu verwenden, gibt es keinen Grund, Systemspeicher oder Rechenzyklen zum Erstellen zu verwenden. Indem Sie Lazy verwenden, um das Orders-Objekt für die verzögerte Initialisierung zu deklarieren, können Sie die Verschwendung von Systemressourcen vermeiden, wenn das Objekt nicht verwendet wird.

  • Wenn Sie ein Objekt haben, dessen Erstellung teuer ist und dessen Erstellung erst nach Abschluss anderer teurer Vorgänge verschoben werden soll. Angenommen, Ihr Programm lädt beim Start mehrere Objektinstanzen, von denen jedoch nur einige sofort erforderlich sind. Sie können die Startleistung des Programms verbessern, indem Sie die Initialisierung der Objekte verschieben, die erst erforderlich sind, wenn die erforderlichen Objekte erstellt wurden.

Amir Rezaei
quelle
3

Eine verzögerte Initialisierung eines Objekts bedeutet, dass seine Erstellung verschoben wird, bis es zum ersten Mal verwendet wird. (In diesem Thema sind die Begriffe "verzögerte Initialisierung" und "verzögerte Instanziierung" synonym.) Die verzögerte Initialisierung wird hauptsächlich verwendet, um die Leistung zu verbessern, verschwenderische Berechnungen zu vermeiden und den Speicherbedarf des Programms zu verringern. Dies sind die häufigsten Szenarien:

Wenn Sie ein Objekt haben, dessen Erstellung teuer ist und das Programm es möglicherweise nicht verwendet. Angenommen, Sie haben ein Kundenobjekt im Speicher, das über eine Orders-Eigenschaft verfügt, die ein großes Array von Order-Objekten enthält, für deren Initialisierung eine Datenbankverbindung erforderlich ist. Wenn der Benutzer niemals auffordert, die Bestellungen anzuzeigen oder die Daten für eine Berechnung zu verwenden, gibt es keinen Grund, Systemspeicher oder Rechenzyklen zum Erstellen zu verwenden. Indem Sie Lazy verwenden, um das Orders-Objekt für die verzögerte Initialisierung zu deklarieren, können Sie die Verschwendung von Systemressourcen vermeiden, wenn das Objekt nicht verwendet wird.

Wenn Sie ein Objekt haben, dessen Erstellung teuer ist, und dessen Erstellung erst nach Abschluss anderer teurer Vorgänge verschoben werden sollen. Angenommen, Ihr Programm lädt beim Start mehrere Objektinstanzen, von denen jedoch nur einige sofort erforderlich sind. Sie können die Startleistung des Programms verbessern, indem Sie die Initialisierung der Objekte verschieben, die erst erforderlich sind, wenn die erforderlichen Objekte erstellt wurden.

Obwohl Sie Ihren eigenen Code schreiben können, um eine verzögerte Initialisierung durchzuführen, empfehlen wir, stattdessen Lazy zu verwenden. Lazy und seine verwandten Typen unterstützen auch die Thread-Sicherheit und bieten eine konsistente Richtlinie zur Weitergabe von Ausnahmen.

Rakesh Kumar
quelle
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
Karthik Krishna Baiju
quelle
1

Was ich bisher über Lazy Init verstanden habe, ist, dass das Programm nicht alle Daten einmal lädt / anfordert. Es wartet auf die Verwendung, bevor es z. ein SQL-Server.

Wenn Sie eine Datenbank mit einer großen Tabelle haben, die mit einer großen Anzahl von Untertabellen verknüpft ist, und Sie nicht die Details benötigen, die aus den anderen Tabels verknüpft wurden, es sei denn, Sie gehen zu "Bearbeiten" oder "Details anzeigen", dann zu Lazy Init. wird der Anwendung helfen, schneller zu werden und zuerst die Detaildaten bei Bedarf "faul zu laden".

In SQL oder LINQ können Sie diese "Einstellung" in Ihrem Datenbankmodell pr festlegen. Datenelement.

Hoffe das macht Sinn für deine Frage?

Ein Webclient (z. B. ein Browser) macht dasselbe. Die Bilder werden nach dem HTML-Code "faul geladen" und AJAX ist auch eine "Art fauler Init", wenn sie richtig verwendet werden.

BerggreenDK
quelle
1

Die bisher erwähnten Datenbankbeispiele sind gut, aber nicht nur auf die Datenzugriffsschicht beschränkt. Sie können dieselben Prinzipien auf jede Situation anwenden, in der Leistung oder Speicher ein Problem darstellen können. Ein gutes Beispiel (obwohl nicht .NET) ist Cocoa, wo Sie warten können, bis der Benutzer ein Fenster anfordert, um es (und die zugehörigen Objekte) tatsächlich von der Schreibfeder zu laden. Dies kann dazu beitragen, die Speichernutzung gering zu halten und das anfängliche Laden der Anwendung zu beschleunigen, insbesondere wenn Sie über Dinge wie Einstellungsfenster sprechen, die, wenn überhaupt, erst zu einem späteren Zeitpunkt benötigt werden.

Marc Charbonneau
quelle