Lazy<T> Třída

Definice

Poskytuje podporu opožděné inicializace.

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 objektu, který je opožděně inicializován.

Dědičnost
Lazy<T>
Odvozené
Atributy

Příklady

Následující příklad ukazuje použití Lazy<T> třídy k zajištění opožděné inicializace s přístupem z více vláken.

Poznámka

Příklad používá Lazy<T>(Func<T>) konstruktor. Ukazuje také použití konstruktoru Lazy<T>(Func<T>, Boolean) (určení true pro isThreadSafe) a Lazy<T>(Func<T>, LazyThreadSafetyMode) konstruktoru (určení LazyThreadSafetyMode.ExecutionAndPublication pro mode). Chcete-li přepnout na jiný konstruktor, stačí změnit, které konstruktory jsou okomentovány.

Příklad znázorňující ukládání výjimek do mezipaměti pomocí stejných konstruktorů najdete v konstruktoru Lazy<T>(Func<T>) .

Příklad definuje LargeObject třídu, která bude inicializována lazily jedním z několika vláken. Čtyři klíčové části kódu znázorňují vytvoření inicializátoru, metody továrny, skutečné inicializace a konstruktoru LargeObject třídy, který zobrazí zprávu při vytvoření objektu. Na začátku Main metody vytvoří příklad lazy inicializátoru LargeObjectpro :

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 továrny ukazuje vytvoření objektu se zástupným symbolem pro další inicializaci:

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

Všimněte si, že první dva oddíly kódu je možné kombinovat pomocí funkce lambda, jak je znázorněno tady:

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)

Příklad pozastaví, aby značil, že nedeterminatní tečka může uplynula dříve, než dojde k opožděné inicializaci. Když stisknete klávesu Enter , příklad vytvoří a spustí tři vlákna. Metoda ThreadProc , která je používána všemi třemi vlákny volá Value vlastnost. Při prvním výskytu LargeObject se instance vytvoří:

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 třídy, který obsahuje poslední část klíče kódu, zobrazí zprávu a zaznamenává identitu inicializačního vlákna. Výstup z programu se zobrazí na konci úplného výpisu kódu.

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

Poznámka

Pro zjednodušení tento příklad používá globální instanci Lazy<T>a všechny metody jsou static (Shared v Visual Basic). Toto nejsou požadavky pro použití opožděné inicializace.

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
'

Poznámky

Použití opožděné inicializace k odvozování vytvoření velkého objektu nebo objektu náročného na prostředky nebo provádění úlohy náročného na zdroje, zejména v případě, že k vytvoření nebo provedení nemusí dojít během doby života programu.

K přípravě na opožděnou inicializaci vytvoříte instanci Lazy<T>. Argument typu objektu Lazy<T> , který vytvoříte, určuje typ objektu, který chcete inicializovat lazily. Konstruktor, který použijete k vytvoření objektu Lazy<T> , určuje vlastnosti inicializace. Při prvním přístupu k Lazy<T>.Value vlastnosti dochází k opožděné inicializaci.

Ve většině případů výběr konstruktoru závisí na vašich odpovědích na dvě otázky:

  • Bude laziálně inicializovaný objekt přístupný z více než jednoho vlákna? Pokud ano, Lazy<T> objekt ho může vytvořit na libovolném vlákně. Můžete použít jeden z jednoduchých konstruktorů, jejichž výchozím chováním je vytvoření objektu bezpečného Lazy<T> pro vlákno, takže se vytvoří pouze jedna instance laziálně vytvořeného objektu bez ohledu na to, kolik vláken se k němu pokusí získat přístup. Chcete-li vytvořit Lazy<T> objekt, který není bezpečný pro vlákno, je nutné použít konstruktor, který umožňuje zadat žádnou bezpečnost vláken.

    Upozornění

    Bezpečné vytvoření vlákna objektu Lazy<T> nechrání lazily inicializovaný objekt. Pokud má více vláken přístup k lazily inicializovanému objektu, musíte vytvořit jeho vlastnosti a metody bezpečné pro přístup s více vlákny.

  • Vyžaduje opožděná inicializace hodně kódu nebo má lazily inicializovaný objekt konstruktor bez parametrů, který dělá vše, co potřebujete, a nevyvolá výjimky? Pokud potřebujete napsat inicializační kód nebo pokud je potřeba zpracovat výjimky, použijte jeden z konstruktorů, který přebírá metodu továrny. Napište inicializační kód v metodě továrny.

Následující tabulka ukazuje, který konstruktor se má zvolit na základě těchto dvou faktorů:

Objekt bude přístupný uživatelem Pokud není vyžadován žádný inicializační kód (konstruktor bez parametrů), použijte Pokud je vyžadován inicializační kód, použijte
Více vláken Lazy<T>() Lazy<T>(Func<T>)
Jedno vlákno Lazy<T>(Boolean) s isThreadSafe nastavenou na false. Lazy<T>(Func<T>, Boolean) s isThreadSafe nastavenou na false.

K určení metody továrny můžete použít výraz lambda. Tím zůstane veškerý inicializační kód na jednom místě. Výraz lambda zachycuje kontext, včetně všech argumentů, které předáte konstruktoru lazily inicializovaného objektu.

Ukládání výjimek do mezipaměti Při použití metod továrny se výjimky ukládají do mezipaměti. To znamená, že pokud metoda továrny vyvolá výjimku při prvním pokusu vlákna o přístup k Value vlastnosti objektu Lazy<T> , stejná výjimka se vyvolá při každém následném pokusu. Tím zajistíte, že každé volání Value vlastnosti vytvoří stejný výsledek a zabrání drobným chybám, ke kterým může dojít v případě, že různé vlákna získají různé výsledky. Zkratka Lazy<T> pro skutečnou hodnotu T , která by jinak byla inicializována v určitém dřívějším bodě, obvykle během spuštění. Selhání v tomto dřívějším bodě je obvykle závažné. Pokud existuje potenciál pro obnovitelnou chybu, doporučujeme vytvořit logiku opakování do rutiny inicializace (v tomto případě metoda továrny), stejně jako kdybyste nepoužívají opožděné inicializace.

Alternativou k uzamčení V některých situacích se můžete chtít vyhnout režii výchozího Lazy<T> zamykání objektu. Ve výjimečných situacích může dojít k zablokování. V takových případech můžete použít Lazy<T>(LazyThreadSafetyMode) nebo Lazy<T>(Func<T>, LazyThreadSafetyMode) konstruktor a zadat LazyThreadSafetyMode.PublicationOnly. To umožňuje Lazy<T> objektu vytvořit kopii lazily inicializovaného objektu na každém z několika vláken, pokud vlákna volají Value vlastnost současně. Objekt Lazy<T> zajišťuje, aby všechna vlákna používala stejnou instanci lazily inicializovaného objektu a zahodí instance, které se nepoužívají. Náklady na snížení režie zamykání jsou proto, že váš program může někdy vytvářet a zahodit další kopie nákladného objektu. Ve většině případů je to nepravděpodobné. Příklady pro konstruktory Lazy<T>(LazyThreadSafetyMode) Lazy<T>(Func<T>, LazyThreadSafetyMode) ukazují toto chování.

Důležité

Když zadáte LazyThreadSafetyMode.PublicationOnly, výjimky se nikdy neukládají do mezipaměti, i když zadáte metodu továrny.

Ekvivalentní konstruktory Kromě povolení použití LazyThreadSafetyMode.PublicationOnly, Lazy<T>(LazyThreadSafetyMode) a Lazy<T>(Func<T>, LazyThreadSafetyMode) konstruktory mohou duplikovat funkce ostatních konstruktorů. Následující tabulka ukazuje hodnoty parametrů, které vytvářejí ekvivalentní chování.

Vytvoření objektu Lazy<T> , který je U konstruktorů, které mají LazyThreadSafetyMode mode parametr, nastavte mode na U konstruktorů, které mají logický isThreadSafe parametr, nastavte isThreadSafe na Pro konstruktory bez parametrů zabezpečení vláken
Plně bezpečné vlákno; pomocí uzamčení zajistí, že hodnotu inicializuje pouze jedno vlákno. ExecutionAndPublication true Všechny tyto konstruktory jsou plně bezpečné.
Není bezpečné vlákno. None false Neužívá se.
Plně bezpečné vlákno; threads race to initialize the value. PublicationOnly Neužívá se. Neužívá se.

Další možnosti Informace o použití Lazy<T> se statickými poli vlákna nebo jako záložní úložiště vlastností naleznete v tématu Lazy Initialization.

Konstruktory

Lazy<T>()

Inicializuje novou instanci Lazy<T> třídy. Když dojde k opožděné inicializaci, použije se konstruktor bez parametrů cílového typu.

Lazy<T>(Boolean)

Inicializuje novou instanci Lazy<T> třídy. Pokud dojde k opožděné inicializaci, použije se konstruktor bez parametrů cílového typu a zadaný režim inicializace.

Lazy<T>(Func<T>)

Inicializuje novou instanci Lazy<T> třídy. Při opožděné inicializaci se použije zadaná inicializační funkce.

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

Inicializuje novou instanci Lazy<T> třídy. Při opožděné inicializaci se použije zadaná inicializační funkce a režim inicializace.

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

Inicializuje novou instanci Lazy<T> třídy, která používá zadanou inicializační funkci a režim zabezpečení vlákna.

Lazy<T>(LazyThreadSafetyMode)

Inicializuje novou instanci Lazy<T> třídy, která používá konstruktor T bez parametrů a zadaný režim zabezpečení vláken.

Lazy<T>(T)

Inicializuje novou instanci Lazy<T> třídy, která používá předinicializovanou zadanou hodnotu.

Vlastnosti

IsValueCreated

Získá hodnotu, která označuje, zda byla vytvořena hodnota pro tuto Lazy<T> instanci.

Value

Získá lazily inicializovanou hodnotu aktuální Lazy<T> instance.

Metody

Equals(Object)

Určí, zda se zadaný objekt rovná aktuálnímu objektu.

(Zděděno od Object)
GetHashCode()

Slouží jako výchozí funkce hash.

(Zděděno od Object)
GetType()

Type Získá aktuální instanci.

(Zděděno od Object)
MemberwiseClone()

Vytvoří použádnou kopii aktuálního souboru Object.

(Zděděno od Object)
ToString()

Vytvoří a vrátí řetězcovou reprezentaci Value vlastnosti pro tuto instanci.

Platí pro

Bezpečný přístup z více vláken

Ve výchozím nastavení jsou všechny veřejné a chráněné členy Lazy<T> třídy bezpečné pro vlákno a mohou být používány souběžně z více vláken. Tyto záruky zabezpečení vláken mohou být odebrány volitelně a na instanci pomocí parametrů pro konstruktory typu.

Viz také