Verzögerte InitialisierungLazy Initialization

Verzögerte Initialisierung eines Objekts bedeutet, dass seine Erstellung bis zur ersten Verwendung verzögert wird.Lazy initialization of an object means that its creation is deferred until it is first used. (In diesem Thema werden die Begriffe verzögerte Initialisierung und verzögerte Instanziierung synonym gebraucht.) Die verzögerte Initialisierung wird vorwiegend verwendet, um die Leistung zu verbessern, aufwendige Berechnungen zu vermeiden und die Speicheranforderungen des Programms zu reduzieren.(For this topic, the terms lazy initialization and lazy instantiation are synonymous.) Lazy initialization is primarily used to improve performance, avoid wasteful computation, and reduce program memory requirements. Die folgenden Szenarios sind die häufigsten:These are the most common scenarios:

  • Sie verfügen über ein Objekt, dessen Erstellung teuer ist und das möglicherweise nicht vom Programm verwendet wird.When you have an object that is expensive to create, and the program might not use it. Angenommen, in Ihrem Speicher befindet sich ein Customer-Objekt mit einer Orders-Eigenschaft, die ein großes Array aus Order-Objekten enthält, das zur Initialisierung eine Datenbankverbindung benötigt.For example, assume that you have in memory a Customer object that has an Orders property that contains a large array of Order objects that, to be initialized, requires a database connection. Fordert der Benutzer nie die Anzeige des Orders-Objekts oder die Verwendung der Daten in einer Berechnung an, ist es nicht notwendig, Systemspeicher oder Berechnungszyklen für seine Erstellung zu verwenden.If the user never asks to display the Orders or use the data in a computation, then there is no reason to use system memory or computing cycles to create it. Systemressourcen können geschont werden, wenn Sie die verzögerte Initialisierung für das Orders-Objekt mithilfe von Lazy<Orders> deklarieren, solange das Objekt nicht verwendet wird.By using Lazy<Orders> to declare the Orders object for lazy initialization, you can avoid wasting system resources when the object is not used.

  • Sie verfügen über ein Objekt, dessen Erstellung teuer ist und das daher erst erstellt werden soll, wenn andere teure Vorgänge abgeschlossen sind.When you have an object that is expensive to create, and you want to defer its creation until after other expensive operations have been completed. Angenommen, das Programm lädt beim Start mehrere Objektinstanzen, von denen allerdings nur einige sofort benötigt werden.For example, assume that your program loads several object instances when it starts, but only some of them are required immediately. Hier kann die Startleistung des Programms verbessert werden, indem die Initialisierung der nicht benötigten Objekte verzögert wird, bis die benötigten Objekte erstellt wurden.You can improve the startup performance of the program by deferring initialization of the objects that are not required until the required objects have been created.

Sie können für die verzögerte Initialisierung Ihren eigenen Code schreiben. Es ist jedoch empfehlenswert, stattdessen Lazy<T> zu verwenden.Although you can write your own code to perform lazy initialization, we recommend that you use Lazy<T> instead. Lazy<T> und die zugehörigen verwandten Typen unterstützen auch die Threadsicherheit und stellen eine konsistente Richtlinie zur Ausnahmeweitergabe bereit.Lazy<T> and its related types also support thread-safety and provide a consistent exception propagation policy.

In der folgenden Tabelle werden die Typen aufgelistet, die .NET Framework Version 4 bereitstellt, um die verzögerte Initialisierung in verschiedenen Szenarios zu aktivieren.The following table lists the types that the .NET Framework version 4 provides to enable lazy initialization in different scenarios.

TypType BeschreibungDescription
Lazy<T> Eine Wrapperklasse, die die Semantik für verzögerte Initialisierung für jeden Klassenbibliotheks- oder benutzerdefinierten Typ bereitstellt.A wrapper class that provides lazy initialization semantics for any class library or user-defined type.
ThreadLocal<T> Ähnlich wie Lazy<T>, außer dass die Semantik für verzögerte Initialisierung threadlokal bereitgestellt wird.Resembles Lazy<T> except that it provides lazy initialization semantics on a thread-local basis. Jeder Thread hat Zugriff auf seinen eigenen eindeutigen Wert.Every thread has access to its own unique value.
LazyInitializer Stellt erweiterte static-Methoden (Shared in Visual Basic) für die verzögerte Initialisierung von Objekten bereit ohne den Mehraufwand einer Klasse.Provides advanced static (Shared in Visual Basic) methods for lazy initialization of objects without the overhead of a class.

Grundlegende verzögerte InitialisierungBasic Lazy Initialization

Verwenden Sie Lazy<MyType> (Lazy(Of MyType) in Visual Basic) wie in folgendem Beispiel gezeigt, um einen Typ mit verzögerter Initialisierung, z.B. MyType, zu definieren.To define a lazy-initialized type, for example, MyType, use Lazy<MyType> (Lazy(Of MyType) in Visual Basic), as shown in the following example. Wird im Lazy<T>-Konstruktor kein Delegat übergeben, wird der umschlossene Typ mithilfe von Activator.CreateInstance beim ersten Zugriff auf die Value-Eigenschaft erstellt.If no delegate is passed in the Lazy<T> constructor, the wrapped type is created by using Activator.CreateInstance when the value property is first accessed. Verfügt der Typ nicht über einen Standardkonstruktor, wird eine Laufzeitausnahme ausgelöst.If the type does not have a default constructor, a run-time exception is thrown.

Im folgenden Beispiel wird angenommen, dass Orders eine Klasse mit einem Array aus Order-Objekten ist, die aus einer Datenbank abgerufen wurden.In the following example, assume that Orders is a class that contains an array of Order objects retrieved from a database. Ein Customer-Objekt enthält eine Instanz von Orders, je nach Benutzeraktion werden die Daten aus dem Orders-Objekt jedoch möglicherweise nicht benötigt.A Customer object contains an instance of Orders, but depending on user actions, the data from the Orders object might not be required.

// Initialize by using default Lazy<T> constructor. The 
// Orders array itself is not created yet.
Lazy<Orders> _orders = new Lazy<Orders>();
' Initialize by using default Lazy<T> constructor. The 
'Orders array itself is not created yet.
Dim _orders As Lazy(Of Orders) = New Lazy(Of Orders)()

Sie können im Lazy<T>-Konstruktor auch einen Delegaten übergeben, der bei der Erstellung eine bestimmte Konstruktorüberladung für den umschlossenen Typ aufruft. Außerdem können Sie die weiteren erforderlichen Initialisierungsschritte wie im folgenden Beispiel gezeigt ausführen.You can also pass a delegate in the Lazy<T> constructor that invokes a specific constructor overload on the wrapped type at creation time, and perform any other initialization steps that are required, as shown in the following example.

// Initialize by invoking a specific constructor on Order when Value
// property is accessed
Lazy<Orders> _orders = new Lazy<Orders>(() => new Orders(100));
' Initialize by invoking a specific constructor on Order 
' when Value property is accessed
Dim _orders As Lazy(Of Orders) = New Lazy(Of Orders)(Function() New Orders(100))

Nachdem das Lazy-Objekt erstellt wurde, wird erst eine Instanz von Orders erstellt, wenn auf die Value-Eigenschaft der Lazy-Variable zum ersten Mal zugegriffen wird.After the Lazy object is created, no instance of Orders is created until the Value property of the Lazy variable is accessed for the first time. Beim ersten Zugriff wird der umschlossene Typ erstellt, zurückgegeben und für einen späteren Zugriff gespeichert.On first access, the wrapped type is created and returned, and stored for any future access.

// We need to create the array only if displayOrders is true
if (displayOrders == true)
{
    DisplayOrders(_orders.Value.OrderData);
}
else
{
    // Don't waste resources getting order data.
}
' We need to create the array only if _displayOrders is true
If _displayOrders = True Then
    DisplayOrders(_orders.Value.OrderData)
Else
    ' Don't waste resources getting order data.
End If

Ein Lazy<T>-Objekt gibt immer das gleiche Objekt oder den gleichen Wert zurück, mit dem es initialisiert wurde.A Lazy<T> object always returns the same object or value that it was initialized with. Daher besitzt die Value-Eigenschaft nur einen Lesezugriff.Therefore, the Value property is read-only. Wenn Value einen Verweistyp speichert, können Sie ihm kein neues Objekt zuweisen.If Value stores a reference type, you cannot assign a new object to it. (Sie können allerdings den Wert seiner festlegbaren öffentlichen Felder und Eigenschaften ändern.) Wenn Value einen Werttyp speichert, können Sie seinen Wert nicht ändern.(However, you can change the value of its settable public fields and properties.) If Value stores a value type, you cannot modify its value. Sie können jedoch eine neue Variable erstellen, indem Sie den Variablenkonstruktor erneut mit neuen Argumenten aufrufen.Nevertheless, you can create a new variable by invoking the variable constructor again by using new arguments.

_orders = new Lazy<Orders>(() => new Orders(10));
_orders = New Lazy(Of Orders)(Function() New Orders(10))

Die neue verzögerte Instanz instanziiert das Orders-Objekt wie die vorherige erst, wenn auf die Value-Eigenschaft erstmals zugegriffen wird.The new lazy instance, like the earlier one, does not instantiate Orders until its Value property is first accessed.

Threadsichere InitialisierungThread-Safe Initialization

Standardmäßig sind Lazy<T>-Objekte threadsicher.By default, Lazy<T> objects are thread-safe. Wird im Konstruktor die Art der Threadsicherheit nicht angegeben, sind die erstellten Lazy<T>-Objekte daher threadsicher.That is, if the constructor does not specify the kind of thread safety, the Lazy<T> objects it creates are thread-safe. In Multithreadszenarios initialisiert der erste Thread, der auf die Value-Eigenschaft eines threadsicheren Lazy<T>-Objekts zugreift, die Eigenschaft für jeden nachfolgenden Zugriff auf allen Threads. Alle Threads nutzen dieselben Daten.In multi-threaded scenarios, the first thread to access the Value property of a thread-safe Lazy<T> object initializes it for all subsequent accesses on all threads, and all threads share the same data. Daher spielt es keine Rolle, welcher Thread das Objekt initialisiert. Auch Racebedingungen haben keine Auswirkung.Therefore, it does not matter which thread initializes the object, and race conditions are benign.

Hinweis

Diese Konsistenz kann durch das Zwischenspeichern von Ausnahmen auf Fehlerbedingungen erweitert werden.You can extend this consistency to error conditions by using exception caching. Weitere Informationen finden Sie im folgenden Abschnitt Ausnahmen bei verzögerten Objekten.For more information, see the next section, Exceptions in Lazy Objects.

Das folgende Beispiel verdeutlicht, dass die gleiche Lazy<int>-Instanz über den gleichen Wert für drei separate Threads verfügt.The following example shows that the same Lazy<int> instance has the same value for three separate threads.

// Initialize the integer to the managed thread id of the 
// first thread that accesses the Value property.
Lazy<int> number = new Lazy<int>(() => Thread.CurrentThread.ManagedThreadId);

Thread t1 = new Thread(() => Console.WriteLine("number on t1 = {0} ThreadID = {1}",
                                        number.Value, Thread.CurrentThread.ManagedThreadId));
t1.Start();

Thread t2 = new Thread(() => Console.WriteLine("number on t2 = {0} ThreadID = {1}",
                                        number.Value, Thread.CurrentThread.ManagedThreadId));
t2.Start();

Thread t3 = new Thread(() => Console.WriteLine("number on t3 = {0} ThreadID = {1}", number.Value,
                                        Thread.CurrentThread.ManagedThreadId));
t3.Start();

// Ensure that thread IDs are not recycled if the 
// first thread completes before the last one starts.
t1.Join();
t2.Join();
t3.Join();

/* Sample Output:
    number on t1 = 11 ThreadID = 11
    number on t3 = 11 ThreadID = 13
    number on t2 = 11 ThreadID = 12
    Press any key to exit.
*/
' Initialize the integer to the managed thread id of the 
' first thread that accesses the Value property.
Dim number As Lazy(Of Integer) = New Lazy(Of Integer)(Function()
                                                          Return Thread.CurrentThread.ManagedThreadId
                                                      End Function)

Dim t1 As New Thread(Sub()
                         Console.WriteLine("number on t1 = {0} threadID = {1}",
                                           number.Value, Thread.CurrentThread.ManagedThreadId)
                     End Sub)
t1.Start()

Dim t2 As New Thread(Sub()
                         Console.WriteLine("number on t2 = {0} threadID = {1}",
                                           number.Value, Thread.CurrentThread.ManagedThreadId)
                     End Sub)
t2.Start()

Dim t3 As New Thread(Sub()
                         Console.WriteLine("number on t3 = {0} threadID = {1}",
                                           number.Value, Thread.CurrentThread.ManagedThreadId)
                     End Sub)
t3.Start()

' Ensure that thread IDs are not recycled if the 
' first thread completes before the last one starts.
t1.Join()
t2.Join()
t3.Join()

' Sample Output:
'       number on t1 = 11 ThreadID = 11
'       number on t3 = 11 ThreadID = 13
'       number on t2 = 11 ThreadID = 12
'       Press any key to exit.

Wenn Sie separate Daten für jeden Thread benötigen, verwenden Sie wie weiter unten beschrieben den ThreadLocal<T>-Typ.If you require separate data on each thread, use the ThreadLocal<T> type, as described later in this topic.

Einige Lazy<T>-Konstruktoren verfügen über einen booleschen Parameter mit dem Namen isThreadSafe, der angibt, ob auf die Value-Eigenschaft von mehreren Threads zugegriffen wird.Some Lazy<T> constructors have a Boolean parameter named isThreadSafe that is used to specify whether the Value property will be accessed from multiple threads. Wenn Sie von nur einem Thread auf die Eigenschaft zugreifen möchten, übergeben Sie false für einen moderaten Leistungsvorteil.If you intend to access the property from just one thread, pass in false to obtain a modest performance benefit. Wenn Sie von mehreren Threads auf die Eigenschaft zugreifen möchten, übergeben Sie true, um die Lazy<T>-Instanz anzuweisen, die Racebedingungen ordnungsgemäß zu behandeln, von denen ein Thread bei der Initialisierung eine Ausnahme auslöst.If you intend to access the property from multiple threads, pass in true to instruct the Lazy<T> instance to correctly handle race conditions in which one thread throws an exception at initialization time.

Einige Lazy<T>-Konstruktoren verfügen über einen LazyThreadSafetyMode-Parameter mit dem Namen mode.Some Lazy<T> constructors have a LazyThreadSafetyMode parameter named mode. Diese Konstruktoren stellen einen zusätzlichen Threadsicherheitsmodus bereit.These constructors provide an additional thread safety mode. Entnehmen Sie der folgenden Tabelle, wie die Threadsicherheit eines Lazy<T>-Objekts von den Konstruktorparametern beeinflusst wird, die die Threadsicherheit angeben.The following table shows how the thread safety of a Lazy<T> object is affected by constructor parameters that specify thread safety. Jeder Konstruktor verfügt über höchstens einen solchen Parameter.Each constructor has at most one such parameter.

Threadsicherheit des ObjektsThread safety of the object mode-Parameter LazyThreadSafetyModeLazyThreadSafetyMode mode parameter Boolescher Parameter isThreadSafeBoolean isThreadSafe parameter Keine ThreadsicherheitsparameterNo thread safety parameters
Vollständig threadsicher; nur ein Thread versucht jeweils, den Wert zu initialisieren.Fully thread-safe; only one thread at a time tries to initialize the value. ExecutionAndPublication true Ja.Yes.
Nicht threadsicher.Not thread-safe. None false Nicht zutreffend.Not applicable.
Vollständig threadsicher; Threads befinden sich im Race, um den Wert zu initialisieren.Fully thread-safe; threads race to initialize the value. PublicationOnly Nicht zutreffend.Not applicable. Nicht zutreffend.Not applicable.

Die Tabelle verdeutlicht, dass die Angabe von LazyThreadSafetyMode.ExecutionAndPublication für den mode-Parameter der Angabe von true für den isThreadSafe-Parameter entspricht, ebenso wie die Angabe von LazyThreadSafetyMode.None der Angabe von false.As the table shows, specifying LazyThreadSafetyMode.ExecutionAndPublication for the mode parameter is the same as specifying true for the isThreadSafe parameter, and specifying LazyThreadSafetyMode.None is the same as specifying false.

Die Angabe von LazyThreadSafetyMode.PublicationOnly ermöglicht mehreren Threads, die Initialisierung der Lazy<T>-Instanz zu versuchen.Specifying LazyThreadSafetyMode.PublicationOnly allows multiple threads to attempt to initialize the Lazy<T> instance. Nur ein Thread kann unter dieser Racebedingung gewinnen. Alle anderen Threads empfangen den Wert, der vom erfolgreichen Thread initialisiert wurde.Only one thread can win this race, and all the other threads receive the value that was initialized by the successful thread. Wird während der Initialisierung für einen Thread eine Ausnahme ausgelöst, empfängt dieser Thread nicht den vom erfolgreichen Thread festgelegten Wert.If an exception is thrown on a thread during initialization, that thread does not receive the value set by the successful thread. Da Ausnahmen nicht zwischengespeichert werden, kann ein nachfolgender Versuch, auf die Value-Eigenschaft zuzugreifen, zu einer erfolgreichen Initialisierung führen.Exceptions are not cached, so a subsequent attempt to access the Value property can result in successful initialization. Darin besteht ein Unterschied zur Behandlung von Ausnahmen in anderen Modi, die im folgenden Abschnitt beschrieben werden.This differs from the way exceptions are treated in other modes, which is described in the following section. Weitere Informationen finden Sie unter der LazyThreadSafetyMode-Enumeration.For more information, see the LazyThreadSafetyMode enumeration.

Ausnahmen bei verzögerten ObjektenExceptions in Lazy Objects

Wie weiter oben erwähnt gibt ein Lazy<T>-Objekt immer das gleiche Objekt oder den gleichen Wert zurück, mit dem es initialisiert wurde. Daher verfügt die Value-Eigenschaft nur über einen Lesezugriff.As stated earlier, a Lazy<T> object always returns the same object or value that it was initialized with, and therefore the Value property is read-only. Wird das Zwischenspeichern von Ausnahmen aktiviert, wird diese Unveränderlichkeit auch auf das Ausnahmeverhalten erweitert.If you enable exception caching, this immutability also extends to exception behavior. Wenn ein Objekt mit verzögerter Initialisierung das Zwischenspeichern von Ausnahmen aktiviert und löst eine Ausnahme durch die Initialisierungsmethode aus bei der Value -Eigenschaft erstmals zugegriffen, die gleiche Ausnahme wird ausgelöst, bei jedem nachfolgenden Versuch, den Zugriff auf die Value Eigenschaft .If a lazy-initialized object has exception caching enabled and throws an exception from its initialization method when the Value property is first accessed, that same exception is thrown on every subsequent attempt to access the Value property. Anders ausgedrückt wird der Konstruktor des umschlossenen Typs selbst in Multithreadszenarios niemals erneut aufgerufen.In other words, the constructor of the wrapped type is never re-invoked, even in multithreaded scenarios. Aus diesem Grund kann das Lazy<T>-Objekt nicht bei einem Zugriff eine Ausnahme auslösen und bei einem nachfolgenden Zugriff einen Wert zurückgeben.Therefore, the Lazy<T> object cannot throw an exception on one access and return a value on a subsequent access.

Das Zwischenspeichern von Ausnahmen wird bei der Verwendung eines beliebigen System.Lazy<T>-Konstruktors aktiviert, der eine Initialisierungsmethode erfordert (valueFactory-Parameter). So wird es beispielsweise aktiviert, wenn Sie den Lazy(T)(Func(T))-Konstruktor verwenden.Exception caching is enabled when you use any System.Lazy<T> constructor that takes an initialization method (valueFactory parameter); for example, it is enabled when you use the Lazy(T)(Func(T))constructor. Erfordert der Konstruktor auch einen LazyThreadSafetyMode-Wert (mode-Parameter), geben Sie LazyThreadSafetyMode.ExecutionAndPublication oder LazyThreadSafetyMode.None an.If the constructor also takes a LazyThreadSafetyMode value (mode parameter), specify LazyThreadSafetyMode.ExecutionAndPublication or LazyThreadSafetyMode.None. Durch die Angabe einer Initialisierungsmethode wird das Zwischenspeichern von Ausnahmen für diese beiden Modi aktiviert.Specifying an initialization method enables exception caching for these two modes. Die Initialisierungsmethode kann sehr einfach sein.The initialization method can be very simple. Sie kann z.B. den Standardkonstruktor für T aufrufen: new Lazy<Contents>(() => new Contents(), mode) in C# bzw. New Lazy(Of Contents)(Function() New Contents()) in Visual Basic.For example, it might call the default constructor for T: new Lazy<Contents>(() => new Contents(), mode) in C#, or New Lazy(Of Contents)(Function() New Contents()) in Visual Basic. Wenn Sie einen System.Lazy<T>-Konstruktor verwenden, der keine Initialisierungsmethode angibt, werden Ausnahmen nicht zwischengespeichert, die vom Standardkonstruktor für T ausgelöst werden.If you use a System.Lazy<T> constructor that does not specify an initialization method, exceptions that are thrown by the default constructor for T are not cached. Weitere Informationen finden Sie unter der LazyThreadSafetyMode-Enumeration.For more information, see the LazyThreadSafetyMode enumeration.

Hinweis

Wenn der isThreadSafe-Konstruktorparameter beim Erstellen eines Lazy<T>-Objekts auf false festgelegt ist oder der mode-Konstruktorparameter auf LazyThreadSafetyMode.None, müssen Sie auf das Lazy<T>-Objekt von einem einzelnen Thread zugreifen oder eine eigene Synchronisierung bereitstellen.If you create a Lazy<T> object with the isThreadSafe constructor parameter set to false or the mode constructor parameter set to LazyThreadSafetyMode.None, you must access the Lazy<T> object from a single thread or provide your own synchronization. Dies gilt für alle Aspekte des Objekts, einschließlich dem Zwischenspeichern von Ausnahmen.This applies to all aspects of the object, including exception caching.

Wie im vorherigen Abschnitt erwähnt, behandeln durch Angabe von LazyThreadSafetyMode.PublicationOnly erstellte Lazy<T>-Objekte Ausnahmen unterschiedlich.As noted in the previous section, Lazy<T> objects created by specifying LazyThreadSafetyMode.PublicationOnly treat exceptions differently. Mit PublicationOnly können mehrere Threads um die Initialisierung der Lazy<T>-Instanz konkurrieren.With PublicationOnly, multiple threads can compete to initialize the Lazy<T> instance. In diesem Fall werden Ausnahmen nicht zwischengespeichert. Die Versuche, auf die Value-Eigenschaft zuzugreifen, können fortgesetzt werden, bis die Initialisierung erfolgreich ist.In this case, exceptions are not cached, and attempts to access the Value property can continue until initialization is successful.

In der folgenden Tabelle werden die Steuerungsmethoden für das Zwischenspeichern von Ausnahmen durch Lazy<T>-Konstruktoren zusammengefasst.The following table summarizes the way the Lazy<T> constructors control exception caching.

KonstruktorConstructor ThreadsicherheitsmodusThread safety mode Verwendet die InitialisierungsmethodeUses initialization method Ausnahmen werden zwischengespeichertExceptions are cached
Lazy(T)()Lazy(T)() (ExecutionAndPublication)(ExecutionAndPublication) NeinNo NeinNo
Lazy(T)(Func(T))Lazy(T)(Func(T)) (ExecutionAndPublication)(ExecutionAndPublication) JaYes JaYes
Lazy(T)(Boolean)Lazy(T)(Boolean) True (ExecutionAndPublication) oder false (None)True (ExecutionAndPublication) or false (None) NeinNo NeinNo
Lazy(T)(Func(T), Boolean)Lazy(T)(Func(T), Boolean) True (ExecutionAndPublication) oder false (None)True (ExecutionAndPublication) or false (None) JaYes JaYes
Lazy(T)(LazyThreadSafetyMode)Lazy(T)(LazyThreadSafetyMode) Vom Benutzer angegebenUser-specified NeinNo NeinNo
Lazy(T)(Func(T), LazyThreadSafetyMode)Lazy(T)(Func(T), LazyThreadSafetyMode) Vom Benutzer angegebenUser-specified JaYes Nein, wenn der Benutzer PublicationOnly angibt; andernfalls ja.No if user specifies PublicationOnly; otherwise, yes.

Implementieren einer Eigenschaft mit verzögerter InitialisierungImplementing a Lazy-Initialized Property

Definieren Sie zum Implementieren einer öffentlichen Eigenschaft mit der verzögerten Initialisierung das Unterstützungsfeld der Eigenschaft als Lazy<T>, und geben Sie die Value-Eigenschaft des get-Accessors der Eigenschaft zurück.To implement a public property by using lazy initialization, define the backing field of the property as a Lazy<T>, and return the Value property from the get accessor of the property.

class Customer
{
    private Lazy<Orders> _orders;
    public string CustomerID {get; private set;}
    public Customer(string id)
    {
        CustomerID = id;
        _orders = new Lazy<Orders>(() =>
        {
            // You can specify any additonal 
            // initialization steps here.
            return new Orders(this.CustomerID);
        });
    }

    public Orders MyOrders
    {
        get
        {
            // Orders is created on first access here.
            return _orders.Value;
        }
    }
}
Class Customer
    Private _orders As Lazy(Of Orders)
    Public Shared CustomerID As String
    Public Sub New(ByVal id As String)
        CustomerID = id
        _orders = New Lazy(Of Orders)(Function()
                                          ' You can specify additional 
                                          ' initialization steps here
                                          Return New Orders(CustomerID)
                                      End Function)

    End Sub
    Public ReadOnly Property MyOrders As Orders

        Get
            Return _orders.Value
        End Get

    End Property

End Class

Die Value-Eigenschaft besitzt nur Lesezugriff. Daher verfügt die Eigenschaft, die sie verfügbar macht, über keinen set-Accessor.The Value property is read-only; therefore, the property that exposes it has no set accessor. Ist eine Lese-/Schreibeigenschaft erforderlich, die durch ein Lazy<T>-Objekt gesichert wird, muss der set-Accessor ein neues Lazy<T>-Objekt erstellen und dem Sicherungsspeicher zuweisen.If you require a read/write property backed by a Lazy<T> object, the set accessor must create a new Lazy<T> object and assign it to the backing store. Der set-Accessor muss einen Lambdaausdruck erstellen, der den an den set-Accessor übergebenen neuen Eigenschaftswert zurückgibt. Dieser Lambdaausdruck muss anschließend an den Konstruktor für das neue Lazy<T>-Objekt übergeben werden.The set accessor must create a lambda expression that returns the new property value that was passed to the set accessor, and pass that lambda expression to the constructor for the new Lazy<T> object. Der nächste Zugriff auf die Value-Eigenschaft führt zur Initialisierung des neuen Lazy<T>. Die zugehörige Value-Eigenschaft gibt danach den neuen Wert zurück, der der Eigenschaft zugewiesen wurde.The next access of the Value property will cause initialization of the new Lazy<T>, and its Value property will thereafter return the new value that was assigned to the property. Der Grund für diese komplizierte Gestaltung besteht darin, dass der in Lazy<T> integrierte Multithreadingschutz beibehalten werden soll.The reason for this convoluted arrangement is to preserve the multithreading protections built into Lazy<T>. Andernfalls müssten die Eigenschaftenaccessoren den ersten von der Value-Eigenschaft zurückgegebenen Wert zwischenspeichern und nur den zwischengespeicherten Wert ändern. Dafür müssten Sie Ihren eigenen threadsicheren Code schreiben.Otherwise, the property accessors would have to cache the first value returned by the Value property and only modify the cached value, and you would have to write your own thread-safe code to do that. Aufgrund der zusätzlichen Initialisierungen, die von einer durch ein Lazy<T>-Objekt gesicherten Lese-/Schreibeigenschaft angefordert werden, ist die Leistung möglicherweise nicht akzeptabel.Because of the additional initializations required by a read/write property backed by a Lazy<T> object, the performance might not be acceptable. Darüber hinaus kann je nach Szenario eine zusätzliche Koordinierung erforderlich sein, um Racebedingungen zwischen Setter und Getter zu vermeiden.Furthermore, depending on the specific scenario, additional coordination might be required to avoid race conditions between setters and getters.

Threadlokale verzögerte InitialisierungThread-Local Lazy Initialization

In einigen Multithreadszenarios möchten Sie jedem Thread möglicherweise eigene private Daten zuweisen.In some multithreaded scenarios, you might want to give each thread its own private data. Diese Daten werden threadlokale Daten genannt.Such data is called thread-local data. Bis .NET Framework Version 3.5 konnte das ThreadStatic-Attribut auf eine statische Variable angewendet werden, um aus ihr eine threadlokale Variable zu machen.In the .NET Framework version 3.5 and earlier, you could apply the ThreadStatic attribute to a static variable to make it thread-local. Die Anwendung des ThreadStatic-Attributs kann jedoch geringfügige Fehler verursachen.However, using the ThreadStatic attribute can lead to subtle errors. Selbst grundlegende Initialisierungsanweisungen führen z.B. dazu, dass die Variable wie im folgenden Beispiel gezeigt nur für den ersten Thread initialisiert wird, der auf sie zugreift.For example, even basic initialization statements will cause the variable to be initialized only on the first thread that accesses it, as shown in the following example.

[ThreadStatic]
static int counter = 1;
<ThreadStatic()>
Shared counter As Integer

Für alle anderen Threads wird die Variable mit ihrem Standardwert (0) initialisiert.On all other threads, the variable will be initialized by using its default value (zero). In .NET Framework Version 4 kann der System.Threading.ThreadLocal<T>-Typ alternativ dazu zum Erstellen einer instanzbasierten, threadlokalen Variable verwendet werden, die für alle Threads mit dem von Ihnen bereitgestellten Action<T>-Delegaten initialisiert wird.As an alternative in the .NET Framework version 4, you can use the System.Threading.ThreadLocal<T> type to create an instance-based, thread-local variable that is initialized on all threads by the Action<T> delegate that you provide. Im folgenden Beispiel erhalten alle Threads, die auf counter zugreifen, den Startwert 1.In the following example, all threads that access counter will see its starting value as 1.

ThreadLocal<int> betterCounter = new ThreadLocal<int>(() => 1);
Dim betterCounter As ThreadLocal(Of Integer) = New ThreadLocal(Of Integer)(Function() 1)

ThreadLocal<T> umschließt sein Objekt auf fast die gleiche Weise wie Lazy<T>, mit folgenden entscheidenden Unterschieden:ThreadLocal<T> wraps its object in much the same way as Lazy<T>, with these essential differences:

  • Jeder Thread initialisiert die threadlokale Variable mit seinen eigenen privaten Daten, auf die von anderen Threads nicht zugegriffen werden kann.Each thread initializes the thread-local variable by using its own private data that is not accessible from other threads.

  • Die ThreadLocal<T>.Value-Eigenschaft verfügt über Lese-/Schreibzugriff und kann beliebig oft geändert werden.The ThreadLocal<T>.Value property is read-write, and can be modified any number of times. Dies kann sich auf die Ausnahmeweitergabe auswirken: So kann ein get-Vorgang z.B. eine Ausnahme auslösen, während der nächste den Wert erfolgreich initialisiert.This can affect exception propagation, for example, one get operation can raise an exception but the next one can successfully initialize the value.

  • ThreadLocal<T> initialisiert seinen umschlossenen Typ mit dem Standardwert des Typs, wenn kein Initialisierungsdelegat bereitgestellt wird.If no initialization delegate is provided, ThreadLocal<T> will initialize its wrapped type by using the default value of the type. In dieser Hinsicht ist ThreadLocal<T> mit dem ThreadStaticAttribute-Attribut konsistent.In this regard, ThreadLocal<T> is consistent with the ThreadStaticAttribute attribute.

Das folgende Beispiel verdeutlicht, dass jeder auf die ThreadLocal<int>-Instanz zugreifende Thread eine eigene eindeutige Kopie der Daten erhält.The following example demonstrates that every thread that accesses the ThreadLocal<int> instance gets its own unique copy of the data.

// Initialize the integer to the managed thread id on a per-thread basis.
ThreadLocal<int> threadLocalNumber = new ThreadLocal<int>(() => Thread.CurrentThread.ManagedThreadId);
Thread t4 = new Thread(() => Console.WriteLine("threadLocalNumber on t4 = {0} ThreadID = {1}",
                                    threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
t4.Start();

Thread t5 = new Thread(() => Console.WriteLine("threadLocalNumber on t5 = {0} ThreadID = {1}",
                                    threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
t5.Start();

Thread t6 = new Thread(() => Console.WriteLine("threadLocalNumber on t6 = {0} ThreadID = {1}",
                                    threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId));
t6.Start();

// Ensure that thread IDs are not recycled if the 
// first thread completes before the last one starts.
t4.Join();
t5.Join();
t6.Join();

/* Sample Output:
   threadLocalNumber on t4 = 14 ThreadID = 14 
   threadLocalNumber on t5 = 15 ThreadID = 15
   threadLocalNumber on t6 = 16 ThreadID = 16 
*/
' Initialize the integer to the managed thread id on a per-thread basis.
Dim threadLocalNumber As New ThreadLocal(Of Integer)(Function() Thread.CurrentThread.ManagedThreadId)
Dim t4 As New Thread(Sub()
                         Console.WriteLine("number on t4 = {0} threadID = {1}",
                                           threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId)
                     End Sub)
t4.Start()

Dim t5 As New Thread(Sub()
                         Console.WriteLine("number on t5 = {0} threadID = {1}",
                                           threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId)
                     End Sub)
t5.Start()

Dim t6 As New Thread(Sub()
                         Console.WriteLine("number on t6 = {0} threadID = {1}",
                                           threadLocalNumber.Value, Thread.CurrentThread.ManagedThreadId)
                     End Sub)
t6.Start()

' Ensure that thread IDs are not recycled if the 
' first thread completes before the last one starts.
t4.Join()
t5.Join()
t6.Join()

'Sample(Output)
'      threadLocalNumber on t4 = 14 ThreadID = 14 
'      threadLocalNumber on t5 = 15 ThreadID = 15
'      threadLocalNumber on t6 = 16 ThreadID = 16 

Threadlokale Variablen in Parallel.For und ForEachThread-Local Variables in Parallel.For and ForEach

Beim parallelen Durchlaufen von Datenquellen mit den Methoden Parallel.For oder Parallel.ForEach können Sie Überladungen mit integrierter Unterstützung für threadlokale Daten verwenden.When you use the Parallel.For method or Parallel.ForEach method to iterate over data sources in parallel, you can use the overloads that have built-in support for thread-local data. Bei diesen Methoden wird die Threadlokalität mithilfe von lokalen Delegaten erzielt, um die Daten zu erstellen, auf sie zuzugreifen und sie zu bereinigen.In these methods, the thread-locality is achieved by using local delegates to create, access, and clean up the data. Weitere Informationen finden Sie unter Vorgehensweise: Schreiben einer Parallel.For-Schleife mit threadlokalen Variablen und Vorgehensweise: Schreiben einer Parallel.ForEach-Schleife mit partitionslokalen Variablen.For more information, see How to: Write a Parallel.For Loop with Thread-Local Variables and How to: Write a Parallel.ForEach Loop with Partition-Local Variables.

Verwenden der verzögerten Initialisierung für Szenarios mit geringem MehraufwandUsing Lazy Initialization for Low-Overhead Scenarios

In Szenarios mit einer großen Anzahl von Objekten, die verzögert initialisiert werden sollen, würde das Umschließen jedes einzelnen Objekts mit einem Lazy<T> möglicherweise zu viel Arbeitsspeicher oder zu viele Computerressourcen erfordern.In scenarios where you have to lazy-initialize a large number of objects, you might decide that wrapping each object in a Lazy<T> requires too much memory or too many computing resources. Möglicherweise bestehen auch strenge Anforderungen an die Art, wie die verzögerte Initialisierung verfügbar gemacht wird.Or, you might have stringent requirements about how lazy initialization is exposed. In diesen Fällen können Sie die static-Methoden (Shared in Visual Basic) der System.Threading.LazyInitializer-Klasse zur Initialisierung jedes Objekts verwenden, ohne es mit einer Instanz von Lazy<T> zu umschließen.In such cases, you can use the static (Shared in Visual Basic) methods of the System.Threading.LazyInitializer class to lazy-initialize each object without wrapping it in an instance of Lazy<T>.

Im folgenden Beispiel wird angenommen, dass einzelne Order-Objekte nur nach Bedarf verzögert initialisiert werden, anstatt ein ganzes Orders-Objekt mit einem Lazy<T>-Objekt zu umschließen.In the following example, assume that, instead of wrapping an entire Orders object in one Lazy<T> object, you have lazy-initialized individual Order objects only if they are required.

// Assume that _orders contains null values, and
// we only need to initialize them if displayOrderInfo is true
if(displayOrderInfo == true)
{
    for (int i = 0; i < _orders.Length; i++)
    {
        // Lazily initialize the orders without wrapping them in a Lazy<T>
        LazyInitializer.EnsureInitialized(ref _orders[i], () =>
            {
                // Returns the value that will be placed in the ref parameter.
                return GetOrderForIndex(i);
            });
    }
}
' Assume that _orders contains null values, and
' we only need to initialize them if displayOrderInfo is true
If displayOrderInfo = True Then


    For i As Integer = 0 To _orders.Length
        ' Lazily initialize the orders without wrapping them in a Lazy(Of T)
        LazyInitializer.EnsureInitialized(_orders(i), Function()
                                                          ' Returns the value that will be placed in the ref parameter.
                                                          Return GetOrderForIndex(i)
                                                      End Function)
    Next
End If

Beachten Sie, dass in diesem Beispiel die Initialisierungsprozedur bei jeder Iteration der Schleife aufgerufen wird.In this example, notice that the initialization procedure is invoked on every iteration of the loop. In Multithreadszenarios ist der erste Thread, der die Initialisierungsprozedur aufruft, derjenige, dessen Wert für alle Threads sichtbar ist.In multi-threaded scenarios, the first thread to invoke the initialization procedure is the one whose value is seen by all threads. Zwar rufen auch spätere Threads die Initialisierungsprozedur auf, doch werden ihre Ergebnisse nicht verwendet.Later threads also invoke the initialization procedure, but their results are not used. Ist diese Art von potenzieller Racebedingung nicht akzeptabel, verwenden Sie die Überladung von LazyInitializer.EnsureInitialized, die ein boolesches Argument und ein Synchronisierungsobjekt erfordert.If this kind of potential race condition is not acceptable, use the overload of LazyInitializer.EnsureInitialized that takes a Boolean argument and a synchronization object.

Siehe auchSee also