Lazy<T> Klass
Definition
Viktigt
En del information gäller för förhandsversionen av en produkt och kan komma att ändras avsevärt innan produkten blir allmänt tillgänglig. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, avseende informationen som visas här.
Ger stöd för lat initiering.
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)
Typparametrar
- T
Den typ av objekt som initieras lazily.
- Arv
-
Lazy<T>
- Härledda
- Attribut
Exempel
I följande exempel visas hur klassen används Lazy<T> för att ge lat initiering åtkomst från flera trådar.
Note
I exemplet används Lazy<T>(Func<T>) konstruktorn. Den visar också användningen av Lazy<T>(Func<T>, Boolean) konstruktorn (anger true för isThreadSafe) och Lazy<T>(Func<T>, LazyThreadSafetyMode) konstruktorn (anger LazyThreadSafetyMode.ExecutionAndPublication för mode). Om du vill växla till en annan konstruktor ändrar du bara vilka konstruktorer som kommenteras ut.
Ett exempel som visar undantagscachelagring med samma konstruktorer finns i Lazy<T>(Func<T>) konstruktorn.
Exemplet definierar en LargeObject klass som initieras lazily av en av flera trådar. De fyra nyckelavsnitten i koden illustrerar skapandet av initieraren, fabriksmetoden, den faktiska initieringen och konstruktorn för LargeObject klassen, som visar ett meddelande när objektet skapas. I början av Main metoden skapar exemplet den trådsäkra lata initieraren för 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)
Fabriksmetoden visar skapandet av objektet, med en platshållare för ytterligare initiering:
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
Observera att de två första kodavsnitten kan kombineras med hjälp av en lambda-funktion, som du ser här:
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)
Exemplet pausar för att indikera att en obestämd period kan förflutit innan en lat initiering inträffar. När du trycker på Retur skapar och startar exemplet tre trådar. Metoden ThreadProc som används av alla tre trådarna anropar Value egenskapen. Första gången detta inträffar skapas instansen LargeObject :
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
Konstruktorn för LargeObject klassen, som innehåller det sista nyckelavsnittet i koden, visar ett meddelande och registrerar identiteten för den initierande tråden. Utdata från programmet visas i slutet av den fullständiga kodlistan.
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
För enkelhetens skull använder det här exemplet en global instans av Lazy<T> och alla metoder är static (Shared i Visual Basic). Det här är inte krav för användning av lat initiering.
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
'
Kommentarer
Använd lat initiering för att skjuta upp skapandet av ett stort eller resursintensivt objekt, eller körningen av en resursintensiv uppgift, särskilt när sådana skapanden eller körningen kanske inte inträffar under programmets livslängd.
För att förbereda för lat initiering skapar du en instans av Lazy<T>. Typargumentet för det Lazy<T> objekt som du skapar anger typen av objekt som du vill initiera lazily. Konstruktorn som du använder för att skapa Lazy<T> objektet avgör initieringens egenskaper. Lat initiering sker första gången egenskapen Lazy<T>.Value används.
I de flesta fall beror valet av konstruktor på dina svar på två frågor:
Kommer det lättinitierade objektet att nås från mer än en tråd? I så fall kan objektet Lazy<T> skapa det på valfri tråd. Du kan använda en av de enkla konstruktorer vars standardbeteende är att skapa ett trådsäkert Lazy<T> objekt, så att endast en instans av det lazily instansierade objektet skapas oavsett hur många trådar som försöker komma åt det. Om du vill skapa ett Lazy<T> objekt som inte är trådsäkert måste du använda en konstruktor som gör att du inte kan ange någon trådsäkerhet.
Caution
Att göra objekttråden Lazy<T> säker skyddar inte det lättinitierade objektet. Om flera trådar kan komma åt det lättinitierade objektet måste du göra dess egenskaper och metoder säkra för multitrådad åtkomst.
Kräver lat initiering mycket kod, eller har det lättinitierade objektet en parameterlös konstruktor som gör allt du behöver och inte utlöser undantag? Om du behöver skriva initieringskod eller om undantag behöver hanteras använder du någon av konstruktorerna som använder en fabriksmetod. Skriv initieringskoden i fabriksmetoden.
I följande tabell visas vilken konstruktor som ska väljas baserat på dessa två faktorer:
| Objektet kommer att nås av | Om ingen initieringskod krävs (parameterlös konstruktor) använder du | Om initieringskoden krävs använder du |
|---|---|---|
| Flera trådar | Lazy<T>() | Lazy<T>(Func<T>) |
| En tråd |
Lazy<T>(Boolean) med isThreadSafe inställt på false. |
Lazy<T>(Func<T>, Boolean) med isThreadSafe inställt på false. |
Du kan använda ett lambda-uttryck för att ange fabriksmetoden. Detta behåller all initieringskod på ett och samma ställe. Lambda-uttrycket avbildar kontexten, inklusive eventuella argument som du skickar till det lättinitierade objektets konstruktor.
Undantagscachelagring När du använder fabriksmetoder cachelagras undantag. Det innebär att om fabriksmetoden utlöser ett undantag första gången en tråd försöker komma åt Value objektets Lazy<T> egenskap, utlöses samma undantag vid varje efterföljande försök. Detta säkerställer att varje anrop till Value egenskapen ger samma resultat och undviker subtila fel som kan uppstå om olika trådar får olika resultat. Står Lazy<T> för en faktisk T som annars skulle ha initierats någon gång tidigare, vanligtvis under starten. Ett fel vid den tidigare tidpunkten är vanligtvis dödligt. Om det finns en risk för ett återställningsbart fel rekommenderar vi att du skapar logiken för återförsök i initieringsrutinen (i det här fallet fabriksmetoden), precis som om du inte använde lat initiering.
Alternativ till låsning I vissa situationer kanske du vill undvika omkostnaderna Lazy<T> för objektets standardlåsningsbeteende. I sällsynta situationer kan det finnas en risk för dödlägen. I sådana fall kan du använda Lazy<T>(LazyThreadSafetyMode) konstruktorn eller Lazy<T>(Func<T>, LazyThreadSafetyMode) och ange LazyThreadSafetyMode.PublicationOnly. På så sätt kan Lazy<T> objektet skapa en kopia av det lättsinnigt initierade objektet på var och en av flera trådar om trådarna anropar Value egenskapen samtidigt. Objektet Lazy<T> ser till att alla trådar använder samma instans av det lättsinnigt initierade objektet och tar bort de instanser som inte används. Kostnaden för att minska låskostnaderna är därför att programmet ibland kan skapa och ta bort extra kopior av ett dyrt objekt. I de flesta fall är detta osannolikt. Exemplen Lazy<T>(LazyThreadSafetyMode) för konstruktorerna och Lazy<T>(Func<T>, LazyThreadSafetyMode) visar det här beteendet.
Important
När du anger LazyThreadSafetyMode.PublicationOnlycachelagras undantag aldrig, även om du anger en fabriksmetod.
Motsvarande konstruktorer Förutom att aktivera användningen av LazyThreadSafetyMode.PublicationOnlyLazy<T>(LazyThreadSafetyMode) kan konstruktorerna och Lazy<T>(Func<T>, LazyThreadSafetyMode) duplicera funktionerna i de andra konstruktorerna. I följande tabell visas de parametervärden som ger motsvarande beteende.
| Så här skapar du ett Lazy<T> objekt som är | För konstruktorer som har en LazyThreadSafetyModemode parameter anger du mode till |
För konstruktorer som har en boolesk isThreadSafe parameter anger du isThreadSafe till |
För konstruktorer utan trådsäkerhetsparametrar |
|---|---|---|---|
| Helt trådsäker; använder låsning för att säkerställa att endast en tråd initierar värdet. | ExecutionAndPublication | true |
Alla sådana konstruktorer är helt trådsäkra. |
| Inte trådsäkert. | None | false |
Ej tillämpbart. |
| Helt trådsäker; trådar ras för att initiera värdet. | PublicationOnly | Ej tillämpbart. | Ej tillämpbart. |
Andra funktioner Information om användningen av Lazy<T> med trådstatiska fält eller som lagringsplats för egenskaper finns i Lazy Initialization (Lat initiering).
Konstruktorer
| Name | Description |
|---|---|
| Lazy<T>() |
Initierar en ny instans av Lazy<T> klassen. När en lat initiering sker används den parameterlösa konstruktorn av måltypen. |
| Lazy<T>(Boolean) |
Initierar en ny instans av Lazy<T> klassen. När en lat initiering sker används den parameterlösa konstruktorn av måltypen och det angivna initieringsläget. |
| Lazy<T>(Func<T>, Boolean) |
Initierar en ny instans av Lazy<T> klassen. När lat initiering inträffar används den angivna initieringsfunktionen och initieringsläget. |
| Lazy<T>(Func<T>, LazyThreadSafetyMode) |
Initierar en ny instans av Lazy<T> klassen som använder den angivna initieringsfunktionen och trådsäkerhetsläget. |
| Lazy<T>(Func<T>) |
Initierar en ny instans av Lazy<T> klassen. När lat initiering inträffar används den angivna initieringsfunktionen. |
| Lazy<T>(LazyThreadSafetyMode) |
Initierar en ny instans av Lazy<T> klassen som använder den parameterlösa konstruktorn |
| Lazy<T>(T) |
Initierar en ny instans av Lazy<T> klassen som använder ett förinitierat angivet värde. |
Egenskaper
| Name | Description |
|---|---|
| IsValueCreated |
Hämtar ett värde som anger om ett värde har skapats för den här instansen Lazy<T> . |
| Value |
Hämtar det lättsinnigt initierade värdet för den aktuella Lazy<T> instansen. |
Metoder
| Name | Description |
|---|---|
| Equals(Object) |
Avgör om det angivna objektet är lika med det aktuella objektet. (Ärvd från Object) |
| GetHashCode() |
Fungerar som standard-hash-funktion. (Ärvd från Object) |
| GetType() |
Hämtar den aktuella instansen Type . (Ärvd från Object) |
| MemberwiseClone() |
Skapar en ytlig kopia av den aktuella Object. (Ärvd från Object) |
| ToString() |
Skapar och returnerar en strängrepresentation av egenskapen för den här instansen Value . |
Gäller för
Trådsäkerhet
Som standard är alla offentliga och skyddade medlemmar i Lazy<T> klassen trådsäkra och kan användas samtidigt från flera trådar. Dessa trådsäkerhetsgarantier kan tas bort valfritt och per instans, med hjälp av parametrar för typens konstruktorer.