Lazy<T> Klas
Definitie
Belangrijk
Bepaalde informatie heeft betrekking op een voorlopige productversie die aanzienlijk kan worden gewijzigd voordat deze wordt uitgebracht. Microsoft biedt geen enkele expliciete of impliciete garanties met betrekking tot de informatie die hier wordt verstrekt.
Biedt ondersteuning voor luie initialisatie.
generic <typename T>
public ref class Lazy
public class Lazy<T>
[System.Runtime.InteropServices.ComVisible(false)]
[System.Serializable]
public class Lazy<T>
type Lazy<'T> = class
[<System.Runtime.InteropServices.ComVisible(false)>]
[<System.Serializable>]
type Lazy<'T> = class
Public Class Lazy(Of T)
Type parameters
- T
Het type object dat lui wordt geïnitialiseerd.
- Overname
-
Lazy<T>
- Afgeleid
- Kenmerken
Voorbeelden
In het volgende voorbeeld ziet u hoe de klasse wordt Lazy<T> gebruikt om luie initialisatie te bieden met toegang vanuit meerdere threads.
Note
In het voorbeeld wordt de Lazy<T>(Func<T>) constructor gebruikt. Het toont ook het gebruik van de Lazy<T>(Func<T>, Boolean) constructor (opgeven voor trueisThreadSafe) en de Lazy<T>(Func<T>, LazyThreadSafetyMode) constructor (opgeven voor LazyThreadSafetyMode.ExecutionAndPublicationmode). Als u wilt overschakelen naar een andere constructor, hoeft u alleen te wijzigen welke constructors als commentaar worden weergegeven.
Zie de Lazy<T>(Func<T>) constructor voor een voorbeeld van het opslaan van uitzonderingen in cache met behulp van dezelfde constructors.
In het voorbeeld wordt een LargeObject klasse gedefinieerd die lazily wordt geïnitialiseerd door een van de verschillende threads. De vier belangrijke secties van code illustreren het maken van de initialisatie, de factorymethode, de werkelijke initialisatie en de constructor van de LargeObject klasse, die een bericht weergeeft wanneer het object wordt gemaakt. Aan het begin van de Main methode maakt het voorbeeld de thread-safe lazy initializer voor LargeObject:
lazyLargeObject = new Lazy<LargeObject>(InitLargeObject);
// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, true);
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject,
// LazyThreadSafetyMode.ExecutionAndPublication);
let lazyLargeObject = Lazy<LargeObject> initLargeObject
// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject,
// LazyThreadSafetyMode.ExecutionAndPublication)
lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject)
' The following lines show how to use other constructors to achieve exactly the
' same result as the previous line:
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, True)
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
' LazyThreadSafetyMode.ExecutionAndPublication)
De factorymethode toont het maken van het object, met een tijdelijke aanduiding voor verdere initialisatie:
static LargeObject InitLargeObject()
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
}
let initLargeObject () =
let large = LargeObject Thread.CurrentThread.ManagedThreadId
// Perform additional initialization here.
large
Private Shared Function InitLargeObject() As LargeObject
Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
' Perform additional initialization here.
Return large
End Function
Houd er rekening mee dat de eerste twee codesecties kunnen worden gecombineerd met behulp van een lambda-functie, zoals hier wordt weergegeven:
lazyLargeObject = new Lazy<LargeObject>(() =>
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
});
let lazyLargeObject = Lazy<LargeObject>(fun () ->
let large = LargeObject Thread.CurrentThread.ManagedThreadId
// Perform additional initialization here.
large)
lazyLargeObject = New Lazy(Of LargeObject)(Function ()
Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
' Perform additional initialization here.
Return large
End Function)
Het voorbeeld onderbreekt, om aan te geven dat een onbepaalde periode kan verstrijken voordat luie initialisatie plaatsvindt. Wanneer u op Enter drukt, wordt in het voorbeeld drie threads gemaakt en gestart. De ThreadProc methode die wordt gebruikt door alle drie de threads roept de Value eigenschap aan. De eerste keer dat dit gebeurt, wordt het LargeObject exemplaar gemaakt:
LargeObject large = lazyLargeObject.Value;
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
// object after creation. You must lock the object before accessing it,
// unless the type is thread safe. (LargeObject is not thread safe.)
lock(large)
{
large.Data[0] = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Initialized by thread {0}; last used by thread {1}.",
large.InitializedBy, large.Data[0]);
}
let large = lazyLargeObject.Value
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
// object after creation. You must lock the object before accessing it,
// unless the type is thread safe. (LargeObject is not thread safe.)
lock large (fun () ->
large.Data[0] <- Thread.CurrentThread.ManagedThreadId
printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")
Dim large As LargeObject = lazyLargeObject.Value
' IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
' object after creation. You must lock the object before accessing it,
' unless the type is thread safe. (LargeObject is not thread safe.)
SyncLock large
large.Data(0) = Thread.CurrentThread.ManagedThreadId
Console.WriteLine("Initialized by thread {0}; last used by thread {1}.", _
large.InitializedBy, large.Data(0))
End SyncLock
De constructor van de LargeObject klasse, die de laatste sleutelsectie van de code bevat, geeft een bericht weer en registreert de identiteit van de initialisatiethread. De uitvoer van het programma wordt weergegeven aan het einde van de volledige codevermelding.
int initBy = 0;
public LargeObject(int initializedBy)
{
initBy = initializedBy;
Console.WriteLine("LargeObject was created on thread id {0}.", initBy);
}
type LargeObject(initBy) =
do
printfn $"LargeObject was created on thread id %i{initBy}."
Private initBy As Integer = 0
Public Sub New(ByVal initializedBy As Integer)
initBy = initializedBy
Console.WriteLine("LargeObject was created on thread id {0}.", initBy)
End Sub
Note
Ter vereenvoudiging gebruikt dit voorbeeld een globaal exemplaar van Lazy<T> en alle methoden zijn static (Shared in Visual Basic). Dit zijn geen vereisten voor het gebruik van luie initialisatie.
using System;
using System.Threading;
class Program
{
static Lazy<LargeObject> lazyLargeObject = null;
static LargeObject InitLargeObject()
{
LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
// Perform additional initialization here.
return large;
}
static void Main()
{
// The lazy initializer is created here. LargeObject is not created until the
// ThreadProc method executes.
lazyLargeObject = new Lazy<LargeObject>(InitLargeObject);
// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, true);
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject,
// LazyThreadSafetyMode.ExecutionAndPublication);
Console.WriteLine(
"\r\nLargeObject is not created until you access the Value property of the lazy" +
"\r\ninitializer. Press Enter to create LargeObject.");
Console.ReadLine();
// Create and start 3 threads, each of which uses LargeObject.
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++)
{
threads[i] = new Thread(ThreadProc);
threads[i].Start();
}
// Wait for all 3 threads to finish.
foreach (Thread t in threads)
{
t.Join();
}
Console.WriteLine("\r\nPress Enter to end the program");
Console.ReadLine();
}
static void ThreadProc(object state)
{
LargeObject large = lazyLargeObject.Value;
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
// object after creation. You must lock the object before accessing it,
// unless the type is thread safe. (LargeObject is not thread safe.)
lock(large)
{
large.Data[0] = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Initialized by thread {0}; last used by thread {1}.",
large.InitializedBy, large.Data[0]);
}
}
}
class LargeObject
{
public int InitializedBy { get { return initBy; } }
int initBy = 0;
public LargeObject(int initializedBy)
{
initBy = initializedBy;
Console.WriteLine("LargeObject was created on thread id {0}.", initBy);
}
public long[] Data = new long[100000000];
}
/* This example produces output similar to the following:
LargeObject is not created until you access the Value property of the lazy
initializer. Press Enter to create LargeObject.
LargeObject was created on thread id 3.
Initialized by thread 3; last used by thread 3.
Initialized by thread 3; last used by thread 4.
Initialized by thread 3; last used by thread 5.
Press Enter to end the program
*/
open System
open System.Threading
type LargeObject(initBy) =
do
printfn $"LargeObject was created on thread id %i{initBy}."
member _.InitializedBy = initBy
member val Data = Array.zeroCreate<int64> 100000000
let initLargeObject () =
let large = LargeObject Thread.CurrentThread.ManagedThreadId
// Perform additional initialization here.
large
// The lazy initializer is created here. LargeObject is not created until the
// ThreadProc method executes.
let lazyLargeObject = Lazy<LargeObject> initLargeObject
// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
// let lazyLargeObject = Lazy<LargeObject>(initLargeObject,
// LazyThreadSafetyMode.ExecutionAndPublication)
let threadProc (state: obj) =
let large = lazyLargeObject.Value
// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
// object after creation. You must lock the object before accessing it,
// unless the type is thread safe. (LargeObject is not thread safe.)
lock large (fun () ->
large.Data[0] <- Thread.CurrentThread.ManagedThreadId
printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")
printfn """
LargeObject is not created until you access the Value property of the lazy
initializer. Press Enter to create LargeObject."""
stdin.ReadLine() |> ignore
// Create and start 3 threads, each of which uses LargeObject.
let threads = Array.zeroCreate 3
for i = 0 to 2 do
threads[i] <- Thread(ParameterizedThreadStart threadProc)
threads[i].Start()
// Wait for all 3 threads to finish.
for t in threads do
t.Join()
printfn "\nPress Enter to end the program"
stdin.ReadLine() |> ignore
// This example produces output similar to the following:
// LargeObject is not created until you access the Value property of the lazy
// initializer. Press Enter to create LargeObject.
//
// LargeObject was created on thread id 3.
// Initialized by thread 3 last used by thread 3.
// Initialized by thread 3 last used by thread 4.
// Initialized by thread 3 last used by thread 5.
//
// Press Enter to end the program
Imports System.Threading
Friend Class Program
Private Shared lazyLargeObject As Lazy(Of LargeObject) = Nothing
Private Shared Function InitLargeObject() As LargeObject
Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
' Perform additional initialization here.
Return large
End Function
Shared Sub Main()
' The lazy initializer is created here. LargeObject is not created until the
' ThreadProc method executes.
lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject)
' The following lines show how to use other constructors to achieve exactly the
' same result as the previous line:
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, True)
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
' LazyThreadSafetyMode.ExecutionAndPublication)
Console.WriteLine(vbCrLf & _
"LargeObject is not created until you access the Value property of the lazy" _
& vbCrLf & "initializer. Press Enter to create LargeObject.")
Console.ReadLine()
' Create and start 3 threads, each of which uses LargeObject.
Dim threads(2) As Thread
For i As Integer = 0 To 2
threads(i) = New Thread(AddressOf ThreadProc)
threads(i).Start()
Next i
' Wait for all 3 threads to finish.
For Each t As Thread In threads
t.Join()
Next t
Console.WriteLine(vbCrLf & "Press Enter to end the program")
Console.ReadLine()
End Sub
Private Shared Sub ThreadProc(ByVal state As Object)
Dim large As LargeObject = lazyLargeObject.Value
' IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
' object after creation. You must lock the object before accessing it,
' unless the type is thread safe. (LargeObject is not thread safe.)
SyncLock large
large.Data(0) = Thread.CurrentThread.ManagedThreadId
Console.WriteLine("Initialized by thread {0}; last used by thread {1}.", _
large.InitializedBy, large.Data(0))
End SyncLock
End Sub
End Class
Friend Class LargeObject
Public ReadOnly Property InitializedBy() As Integer
Get
Return initBy
End Get
End Property
Private initBy As Integer = 0
Public Sub New(ByVal initializedBy As Integer)
initBy = initializedBy
Console.WriteLine("LargeObject was created on thread id {0}.", initBy)
End Sub
Public Data(99999999) As Long
End Class
' This example produces output similar to the following:
'
'LargeObject is not created until you access the Value property of the lazy
'initializer. Press Enter to create LargeObject.
'
'LargeObject was created on thread id 3.
'Initialized by thread 3; last used by thread 3.
'Initialized by thread 3; last used by thread 5.
'Initialized by thread 3; last used by thread 4.
'
'Press Enter to end the program
'
Opmerkingen
Gebruik luie initialisatie om het maken van een groot of resource-intensief object of de uitvoering van een resource-intensieve taak uit te stellen, met name wanneer een dergelijke creatie of uitvoering mogelijk niet plaatsvindt tijdens de levensduur van het programma.
Als u zich wilt voorbereiden op luie initialisatie, maakt u een exemplaar van Lazy<T>. Het typeargument van het Lazy<T> object dat u maakt, geeft het type object op dat u lui wilt initialiseren. De constructor die u gebruikt om het Lazy<T> object te maken, bepaalt de kenmerken van de initialisatie. Lazy-initialisatie vindt plaats de eerste keer dat de eigenschap Lazy<T>.Value wordt geopend.
In de meeste gevallen is het kiezen van een constructor afhankelijk van uw antwoorden op twee vragen:
Wordt het lui geitialiseerde object geopend vanuit meer dan één thread? Als dat het geval is, kan het Lazy<T> object het maken op een thread. U kunt een van de eenvoudige constructors gebruiken waarvan het standaardgedrag is om een thread-veilig Lazy<T> object te maken, zodat slechts één exemplaar van het vertraagde geïnstantieerde object wordt gemaakt, ongeacht het aantal threads dat toegang probeert te krijgen. Als u een Lazy<T> object wilt maken dat niet threadveilig is, moet u een constructor gebruiken waarmee u geen threadveiligheid kunt opgeven.
Caution
Als u de Lazy<T> objectthread veilig maakt, wordt het lui geitialiseerde object niet beschermd. Als meerdere threads toegang hebben tot het luis geïnitialiseerde object, moet u de eigenschappen en methoden veilig maken voor multithreaded-toegang.
Is voor luie initialisatie veel code vereist of heeft het lui geïnitialiseerde object een parameterloze constructor die alles doet wat u nodig hebt en geen uitzonderingen genereert? Als u initialisatiecode moet schrijven of als uitzonderingen moeten worden verwerkt, gebruikt u een van de constructors die een factory-methode gebruiken. Schrijf uw initialisatiecode in de factory-methode.
In de volgende tabel ziet u welke constructor u wilt kiezen, op basis van deze twee factoren:
| Het object wordt geopend door | Als er geen initialisatiecode is vereist (parameterloze constructor), gebruikt u | Als initialisatiecode is vereist, gebruikt u |
|---|---|---|
| Meerdere threads | Lazy<T>() | Lazy<T>(Func<T>) |
| Eén thread |
Lazy<T>(Boolean) met isThreadSafe ingesteld op false. |
Lazy<T>(Func<T>, Boolean) met isThreadSafe ingesteld op false. |
U kunt een lambda-expressie gebruiken om de factorymethode op te geven. Hierdoor blijft alle initialisatiecode op één plaats behouden. De lambda-expressie legt de context vast, inclusief argumenten die u doorgeeft aan de constructor van het lui geïnitialiseerde object.
Uitzondering opslaan in cache Wanneer u factory-methoden gebruikt, worden uitzonderingen in de cache opgeslagen. Als de factorymethode een uitzondering genereert wanneer een thread voor het eerst probeert toegang te krijgen tot de Value eigenschap van het Lazy<T> object, wordt dezelfde uitzondering gegenereerd bij elke volgende poging. Dit zorgt ervoor dat elke aanroep van de Value eigenschap hetzelfde resultaat produceert en subtiele fouten voorkomt die kunnen optreden als verschillende threads verschillende resultaten krijgen. De Lazy<T> staat voor een werkelijke T waarde die anders eerder zou zijn geïnitialiseerd, meestal tijdens het opstarten. Een fout op dat eerdere punt is meestal fataal. Als er sprake is van een herstelbare fout, raden we u aan om de logica voor opnieuw proberen in te bouwen in de initialisatieroutine (in dit geval de factorymethode), net zoals u zou doen als u geen luie initialisatie zou gebruiken.
Alternatief voor vergrendelen In bepaalde situaties wilt u de overhead van het standaardvergrendelingsgedrag van het Lazy<T> object voorkomen. In zeldzame situaties is er mogelijk sprake van impasses. In dergelijke gevallen kunt u de Lazy<T>(LazyThreadSafetyMode) of Lazy<T>(Func<T>, LazyThreadSafetyMode) constructor gebruiken en opgeven LazyThreadSafetyMode.PublicationOnly. Hierdoor kan het Lazy<T> object een kopie maken van het vertraagd geïnitialiseerde object op elk van verschillende threads als de threads de Value eigenschap tegelijkertijd aanroepen. Het Lazy<T> object zorgt ervoor dat alle threads gebruikmaken van hetzelfde exemplaar van het lui geïnitialiseerde object en dat de exemplaren die niet worden gebruikt, worden verwijderd. De kosten voor het verminderen van de vergrendelingsoverhead zijn dus dat uw programma soms extra kopieën van een duur object kan maken en negeren. In de meeste gevallen is dit onwaarschijnlijk. In de voorbeelden voor de Lazy<T>(LazyThreadSafetyMode) en Lazy<T>(Func<T>, LazyThreadSafetyMode) constructors wordt dit gedrag gedemonstreerd.
Important
Wanneer u opgeeft LazyThreadSafetyMode.PublicationOnly, worden uitzonderingen nooit in de cache opgeslagen, zelfs als u een factory-methode opgeeft.
Equivalente constructors Naast het inschakelen van het gebruik van LazyThreadSafetyMode.PublicationOnly, kunnen de Lazy<T>(LazyThreadSafetyMode) en Lazy<T>(Func<T>, LazyThreadSafetyMode) constructors de functionaliteit van de andere constructors dupliceren. In de volgende tabel ziet u de parameterwaarden die gelijkwaardig gedrag produceren.
| Een object maken Lazy<T> dat is | Voor constructors met een LazyThreadSafetyModemode parameter, ingesteld op mode |
Voor constructors met een Booleaanse isThreadSafe parameter, ingesteld op isThreadSafe |
Voor constructors zonder threadveiligheidsparameters |
|---|---|---|---|
| Volledig thread veilig; gebruikt vergrendeling om ervoor te zorgen dat slechts één thread de waarde initialiseert. | ExecutionAndPublication | true |
Alle dergelijke constructors zijn volledig thread-veilig. |
| Niet thread veilig. | None | false |
Niet van toepassing. |
| Volledig thread veilig; threads racen om de waarde te initialiseren. | PublicationOnly | Niet van toepassing. | Niet van toepassing. |
Andere mogelijkheden Zie Lazy<T> voor informatie over het gebruik van thread-statische velden of als back-uparchief voor eigenschappen.
Constructors
| Name | Description |
|---|---|
| Lazy<T>() |
Initialiseert een nieuw exemplaar van de Lazy<T> klasse. Wanneer luie initialisatie plaatsvindt, wordt de parameterloze constructor van het doeltype gebruikt. |
| Lazy<T>(Boolean) |
Initialiseert een nieuw exemplaar van de Lazy<T> klasse. Wanneer luie initialisatie plaatsvindt, worden de parameterloze constructor van het doeltype en de opgegeven initialisatiemodus gebruikt. |
| Lazy<T>(Func<T>, Boolean) |
Initialiseert een nieuw exemplaar van de Lazy<T> klasse. Wanneer luie initialisatie plaatsvindt, worden de opgegeven initialisatiefunctie en de initialisatiemodus gebruikt. |
| Lazy<T>(Func<T>, LazyThreadSafetyMode) |
Initialiseert een nieuw exemplaar van de Lazy<T> klasse die gebruikmaakt van de opgegeven initialisatiefunctie en de threadveiligheidsmodus. |
| Lazy<T>(Func<T>) |
Initialiseert een nieuw exemplaar van de Lazy<T> klasse. Wanneer luie initialisatie plaatsvindt, wordt de opgegeven initialisatiefunctie gebruikt. |
| Lazy<T>(LazyThreadSafetyMode) |
Initialiseert een nieuw exemplaar van de Lazy<T> klasse die gebruikmaakt van de parameterloze constructor van |
| Lazy<T>(T) |
Initialiseert een nieuwe instantie van de Lazy<T> klasse die gebruikmaakt van een vooraf geïnitialiseerde opgegeven waarde. |
Eigenschappen
| Name | Description |
|---|---|
| IsValueCreated |
Hiermee wordt een waarde opgehaald die aangeeft of er een waarde is gemaakt voor dit Lazy<T> exemplaar. |
| Value |
Hiermee haalt u de vertraagde geïnitialiseerde waarde van het huidige Lazy<T> exemplaar op. |
Methoden
| Name | Description |
|---|---|
| Equals(Object) |
Bepaalt of het opgegeven object gelijk is aan het huidige object. (Overgenomen van Object) |
| GetHashCode() |
Fungeert als de standaardhashfunctie. (Overgenomen van Object) |
| GetType() |
Hiermee haalt u de Type huidige instantie op. (Overgenomen van Object) |
| MemberwiseClone() |
Hiermee maakt u een ondiepe kopie van de huidige Object. (Overgenomen van Object) |
| ToString() |
Hiermee maakt en retourneert u een tekenreeksweergave van de Value eigenschap voor dit exemplaar. |
Van toepassing op
Veiligheid thread
Standaard zijn alle openbare en beveiligde leden van de Lazy<T> klasse thread veilig en kunnen ze gelijktijdig worden gebruikt vanuit meerdere threads. Deze garanties voor thread-veiligheid kunnen optioneel en per exemplaar worden verwijderd met behulp van parameters voor de constructors van het type.