Lazy<T> Klasa

Definicja

Zapewnia obsługę inicjowania leniwego.

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)

Parametry typu

T

Typ obiektu, który jest inicjowany leniwie.

Dziedziczenie
Lazy<T>
Pochodne
Atrybuty

Przykłady

W poniższym przykładzie pokazano użycie klasy w celu zapewnienia leniwej inicjalizacji Lazy<T> z dostępem z wielu wątków.

Uwaga

W przykładzie użyto konstruktora Lazy<T>(Func<T>) . Demonstruje również użycie Lazy<T>(Func<T>, Boolean) konstruktora (określenie true dla isThreadSafeparametru Lazy<T>(Func<T>, LazyThreadSafetyMode) ) i konstruktora (określenie wartości LazyThreadSafetyMode.ExecutionAndPublication mode). Aby przełączyć się na inny konstruktor, po prostu zmień, które konstruktory są komentowane.

Aby zapoznać się z przykładem buforowania wyjątków przy użyciu tych samych konstruktorów, zobacz Lazy<T>(Func<T>) konstruktor.

W przykładzie zdefiniowano klasę LargeObject , która zostanie zainicjowana leniwie przez jeden z kilku wątków. Cztery kluczowe sekcje kodu ilustrują tworzenie inicjatora, metodę fabryki, rzeczywistą inicjację i konstruktor LargeObject klasy, który wyświetla komunikat podczas tworzenia obiektu. Na początku Main metody przykład tworzy bezpieczny wątkowo inicjator leniwy dla elementu 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)

Metoda fabryki przedstawia tworzenie obiektu z symbolem zastępczym na potrzeby dalszej inicjowania:

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

Należy pamiętać, że dwie pierwsze sekcje kodu można połączyć przy użyciu funkcji lambda, jak pokazano poniżej:

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)

Przykład wstrzymuje się, aby wskazać, że nieokreślony okres może upłynąć przed wystąpieniem inicjowania leniwego. Po naciśnięciu klawisza Enter przykład tworzy i uruchamia trzy wątki. Metoda ThreadProc używana przez wszystkie trzy wątki wywołuje Value właściwość . Po raz pierwszy LargeObject zostanie utworzone wystąpienie:

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

Konstruktor LargeObject klasy, która zawiera ostatnią sekcję klucza kodu, wyświetla komunikat i rejestruje tożsamość wątku inicjującego. Dane wyjściowe z programu są wyświetlane na końcu pełnej listy kodu.

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

Uwaga

Dla uproszczenia w tym przykładzie użyto globalnego wystąpienia klasy Lazy<T>, a wszystkie metody to static (Shared w Visual Basic). Nie są wymogi, od których zależy możliwość inicjowania z opóźnieniem.

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
'

Uwagi

Użyj inicjowania z opóźnieniem, aby odroczyć tworzenie dużego lub intensywnie korzystającego z zasobów obiektu lub wykonywanie zadania intensywnie korzystającego z zasobów, szczególnie w przypadku, gdy takie tworzenie lub wykonywanie może nie wystąpić w okresie istnienia programu.

Aby przygotować się do inicjowania z opóźnieniem, należy utworzyć wystąpienie klasy Lazy<T>. Argument typu tworzonego Lazy<T> obiektu określa typ obiektu, który chcesz zainicjować leniwie. Konstruktor, którego używasz do utworzenia Lazy<T> obiektu, określa cechy inicjowania. Inicjowanie z opóźnieniem występuje podczas pierwszego uzyskiwania Lazy<T>.Value dostępu do właściwości.

W większości przypadków wybór konstruktora zależy od odpowiedzi na dwa pytania:

  • Czy obiekt zainicjowany w sposób leniwy będzie uzyskiwany z więcej niż jednego wątku? Jeśli tak, Lazy<T> obiekt może utworzyć go w dowolnym wątku. Można użyć jednego z prostych konstruktorów, których zachowaniem domyślnym jest utworzenie obiektu bezpiecznego Lazy<T> wątkowo, dzięki czemu tylko jedno wystąpienie lazicznie utworzonego obiektu jest tworzone bez względu na to, ile wątków próbuje uzyskać do niego dostęp. Aby utworzyć Lazy<T> obiekt, który nie jest bezpieczny wątkiem, należy użyć konstruktora, który umożliwia określenie bezpieczeństwa wątku.

    Przestroga

    Zapewnienie bezpieczeństwa wątku Lazy<T> obiektu nie chroni leniwie zainicjowanego obiektu. Jeśli wiele wątków może uzyskać dostęp do leniwie zainicjowanego obiektu, należy zapewnić bezpieczeństwo jego właściwości i metod w celu uzyskania dostępu wielowątkowego.

  • Czy leniwa inicjacja wymaga dużo kodu lub czy leniwie zainicjowany obiekt ma konstruktor bez parametrów, który robi wszystko, czego potrzebujesz i nie zgłasza wyjątków? Jeśli musisz napisać kod inicjowania lub jeśli należy obsłużyć wyjątki, użyj jednego z konstruktorów, który przyjmuje metodę fabryki. Napisz kod inicjowania w metodzie factory.

W poniższej tabeli przedstawiono konstruktor do wyboru na podstawie tych dwóch czynników:

Dostęp do obiektu będzie uzyskiwany przez Jeśli nie jest wymagany kod inicjowania (konstruktor bez parametrów), użyj polecenia Jeśli kod inicjowania jest wymagany, użyj polecenia
Wiele wątków Lazy<T>() Lazy<T>(Func<T>)
Jeden wątek Lazy<T>(Boolean) z ustawioną wartością isThreadSafe false. Lazy<T>(Func<T>, Boolean) z ustawioną wartością isThreadSafe false.

Aby określić metodę fabryki, można użyć wyrażenia lambda. Spowoduje to zachowanie całego kodu inicjowania w jednym miejscu. Wyrażenie lambda przechwytuje kontekst, w tym wszelkie argumenty przekazywane do konstruktora zainicjowanego obiektu.

Buforowanie wyjątków W przypadku korzystania z metod fabrycznych wyjątki są buforowane. Oznacza to, że jeśli metoda fabryki zgłasza wyjątek przy pierwszym próbie uzyskania dostępu do Value właściwości Lazy<T> obiektu, ten sam wyjątek jest zgłaszany podczas każdej kolejnej próby. Dzięki temu każde wywołanie Value właściwości generuje ten sam wynik i pozwala uniknąć drobnych błędów, które mogą wystąpić, jeśli różne wątki uzyskują różne wyniki. Oznacza Lazy<T> wartość rzeczywistą T , która w przeciwnym razie zostałaby zainicjowana w pewnym wcześniejszym momencie, zwykle podczas uruchamiania. Awaria w tym wcześniejszym momencie jest zwykle śmiertelna. Jeśli istnieje potencjał możliwego do odzyskania awarii, zalecamy skompilowanie logiki ponawiania prób w procedurze inicjowania (w tym przypadku metoda fabryki), tak jak w przypadku braku leniwego inicjowania.

Alternatywa dla blokady W niektórych sytuacjach warto uniknąć narzutu domyślnego Lazy<T> zachowania blokady obiektu. W rzadkich sytuacjach może istnieć potencjał zakleszczenia. W takich przypadkach można użyć konstruktora Lazy<T>(LazyThreadSafetyMode) lub Lazy<T>(Func<T>, LazyThreadSafetyMode) i określić LazyThreadSafetyMode.PublicationOnly. Dzięki temu obiekt może Lazy<T> utworzyć kopię leniwie zainicjowanego obiektu na każdym z kilku wątków, jeśli wątki wywołają Value właściwość jednocześnie. Obiekt Lazy<T> zapewnia, że wszystkie wątki używają tego samego wystąpienia obiektu zainicjowanego w sposób leniwy i odrzuca wystąpienia, które nie są używane. W związku z tym koszt zmniejszenia obciążenia związane z blokowaniem polega na tym, że program może czasami tworzyć i odrzucać dodatkowe kopie kosztownego obiektu. W większości przypadków jest to mało prawdopodobne. Przykłady konstruktorów Lazy<T>(LazyThreadSafetyMode) i Lazy<T>(Func<T>, LazyThreadSafetyMode) pokazują to zachowanie.

Ważne

Po określeniu LazyThreadSafetyMode.PublicationOnlyparametru wyjątki nigdy nie są buforowane, nawet jeśli określisz metodę fabryki.

Równoważne konstruktory Oprócz włączania używania LazyThreadSafetyMode.PublicationOnlykonstruktorów Lazy<T>(LazyThreadSafetyMode) i Lazy<T>(Func<T>, LazyThreadSafetyMode) mogą zduplikować funkcjonalność innych konstruktorów. W poniższej tabeli przedstawiono wartości parametrów, które generują równoważne zachowanie.

Aby utworzyć Lazy<T> obiekt, który jest W przypadku konstruktorów, które mają LazyThreadSafetyMode mode parametr, ustaw wartość na mode W przypadku konstruktorów, które mają parametr logicznyisThreadSafe, ustaw wartość na isThreadSafe Konstruktory bez parametrów bezpieczeństwa wątku
W pełni bezpieczne wątki; używa blokady, aby upewnić się, że tylko jeden wątek inicjuje wartość. ExecutionAndPublication true Wszystkie takie konstruktory są w pełni bezpieczne wątkami.
Nie bezpieczne wątki. None false Nie dotyczy.
W pełni bezpieczne wątki; wątki ścigają się, aby zainicjować wartość. PublicationOnly Nie dotyczy. Nie dotyczy.

Inne możliwości Aby uzyskać informacje o użyciu Lazy<T> z polami statycznymi wątków lub jako magazynem zaplecza dla właściwości, zobacz Inicjowanie z opóźnieniem.

Konstruktory

Lazy<T>()

Inicjuje nowe wystąpienie klasy Lazy<T>. Gdy wystąpi inicjowanie z opóźnieniem, używany jest konstruktor bez parametrów typu docelowego.

Lazy<T>(Boolean)

Inicjuje nowe wystąpienie klasy Lazy<T>. W przypadku wystąpienia inicjowania z opóźnieniem używany jest konstruktor bez parametrów typu docelowego i określony tryb inicjowania.

Lazy<T>(Func<T>)

Inicjuje nowe wystąpienie klasy Lazy<T>. W przypadku wystąpienia inicjowania z opóźnieniem jest używana określona funkcja inicjowania.

Lazy<T>(Func<T>, Boolean)

Inicjuje nowe wystąpienie klasy Lazy<T>. W przypadku wystąpienia inicjowania z opóźnieniem jest używana określona funkcja inicjowania i tryb inicjowania.

Lazy<T>(Func<T>, LazyThreadSafetyMode)

Inicjuje Lazy<T> nowe wystąpienie klasy, które używa określonej funkcji inicjowania i trybu bezpieczeństwa wątków.

Lazy<T>(LazyThreadSafetyMode)

Inicjuje Lazy<T> nowe wystąpienie klasy, które używa konstruktora bez parametrów i określonego trybu bezpieczeństwa wątków T .

Lazy<T>(T)

Inicjuje Lazy<T> nowe wystąpienie klasy, które używa wstępnie określonej wartości.

Właściwości

IsValueCreated

Pobiera wartość wskazującą, czy dla tego Lazy<T> wystąpienia została utworzona wartość.

Value

Pobiera leniwie zainicjowaną wartość bieżącego Lazy<T> wystąpienia.

Metody

Equals(Object)

Określa, czy dany obiekt jest taki sam, jak bieżący obiekt.

(Odziedziczone po Object)
GetHashCode()

Służy jako domyślna funkcja skrótu.

(Odziedziczone po Object)
GetType()

Type Pobiera wartość bieżącego wystąpienia.

(Odziedziczone po Object)
MemberwiseClone()

Tworzy płytkią kopię bieżącego Objectelementu .

(Odziedziczone po Object)
ToString()

Tworzy i zwraca reprezentację Value ciągu właściwości dla tego wystąpienia.

Dotyczy

Bezpieczeństwo wątkowe

Domyślnie wszystkie publiczne i chronione elementy członkowskie Lazy<T> klasy są bezpieczne wątkami i mogą być używane współbieżnie z wielu wątków. Te gwarancje bezpieczeństwa wątków można usunąć opcjonalnie i na wystąpienie przy użyciu parametrów do konstruktorów typu.

Zobacz też