Sdílet prostřednictvím


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 lazily inicializován.

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

Příklady

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

Poznámka

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

Příklad, který ukazuje ukládání výjimek do mezipaměti pomocí stejných konstruktorů, viz Lazy<T>(Func<T>) konstruktor.

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 třídy LargeObject, který zobrazí zprávu při vytvoření objektu. Na začátku metody Main příklad vytvoří opožděný inicializátor s bezpečným vláknem pro 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 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í dvě části kódu lze zkombinovat pomocí funkce lambda, jak je znázorněno zde:

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 se pozastaví a označuje, že nedeterminační období může uplynout před opožděným inicializací. Když stisknete klávesu Enter, příklad vytvoří a spustí tři vlákna. Metoda ThreadProc, která se používá všemi třemi vlákny, volá Value vlastnost. Když k tomu dojde poprvé, vytvoří se instance 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

Konstruktor třídy LargeObject, 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 jazyce Visual Basic). Nejedná se o požadavky na 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

Pomocí opožděné inicializace odložte vytvoření velkého objektu nebo objektu náročného na prostředky nebo provádění úlohy náročné na prostředky, zejména pokud k vytvoření nebo spuštění nemusí dojít během životnosti programu.

Chcete-li se připravit na opožděné inicializace, vytvoříte instanci Lazy<T>. Typ argumentu Lazy<T> objektu, který vytvoříte, určuje typ objektu, který chcete inicializovat lazily. Konstruktor, který použijete k vytvoření Lazy<T> objektu, určuje vlastnosti inicializace. Opožděné inicializace nastane při prvním přístupu Lazy<T>.Value vlastnost.

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

  • Bude líný inicializovaný objekt přístupný z více než jednoho vlákna? Pokud ano, objekt Lazy<T> jej může vytvořit v 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 pro přístup z více vláken Lazy<T>, takže se vytvoří pouze jedna instance lazily instance objektu bez ohledu na to, kolik vláken se k němu pokouší získat přístup. Chcete-li vytvořit objekt Lazy<T>, který není bezpečný z více vláken, je nutné použít konstruktor, který umožňuje zadat bez zabezpečení vlákna.

    Opatrnost

    Zajištění bezpečnosti vlákna objektu Lazy<T> neochrání lazily inicializovaný objekt. Pokud má více vláken přístup k lazily inicializovanému objektu, je nutné, aby jeho vlastnosti a metody byly bezpečné pro vícevláknový přístup.

  • 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é používají 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ý pomocí Pokud není vyžadován žádný inicializační kód (konstruktor bez parametrů), použijte Pokud se vyžaduje inicializační kód, použijte
Více vláken Lazy<T>() Lazy<T>(Func<T>)
Jedno vlákno Lazy<T>(Boolean) s isThreadSafe nastaveným na false. Lazy<T>(Func<T>, Boolean) s isThreadSafe nastaveným 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 líně inicializovanému konstruktoru 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 vlastnost Lazy<T> objektu, stejná výjimka je vyvolán při každém následném pokusu. Tím zajistíte, že každé volání vlastnosti Value vytvoří stejný výsledek a vyhne se 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. Lazy<T> představuje skutečný T, který by jinak byl inicializován v určitém dřívějším bodě, obvykle během spouštění. Selhání v tomto dřívějším bodě je obvykle závažné. Pokud existuje potenciál obnovitelného selhání, doporučujeme sestavit logiku opakování do inicializační rutiny (v tomto případě metodu továrny), stejně jako kdybyste nepoužívající opožděné inicializace.

Alternativu k uzamčení V určitých situacích můžete chtít zabránit režii výchozího zamykání objektu Lazy<T>. 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> objekt 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, že všechna vlákna používají stejnou instanci lazily inicializovaného objektu a zahodí instance, které se nepoužívají. Proto náklady na snížení režie zamykání spočívá v tom, ž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 Lazy<T>(LazyThreadSafetyMode) a Lazy<T>(Func<T>, LazyThreadSafetyMode) konstruktory 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.PublicationOnlymohou konstruktory Lazy<T>(LazyThreadSafetyMode) a Lazy<T>(Func<T>, LazyThreadSafetyMode) 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í parametr LazyThreadSafetyModemode, nastavte mode na U konstruktorů, které mají logický isThreadSafe parametr, nastavte isThreadSafe na Pro konstruktory bez parametrů zabezpečení vláken
Plně bezpečný závit; pomocí uzamčení zajistí, že hodnotu inicializuje pouze jedno vlákno. ExecutionAndPublication true Všechny tyto konstruktory jsou plně bezpečné pro přístup z více vláken.
Není bezpečné vlákno. None false Nelze použít.
Plně bezpečný závit; threads race to initialize the value. PublicationOnly Nelze použít. Nelze použít.

Další funkce Informace o použití Lazy<T> se statickými poli vlákna nebo jako záložní úložiště vlastností najdete v tématu Opožděná inicializace.

Konstruktory

Lazy<T>()

Inicializuje novou instanci Lazy<T> třídy. Pokud 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. Když 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. Když dojde k opožděné inicializaci, použije se zadaná inicializační funkce.

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

Inicializuje novou instanci Lazy<T> třídy. Pokud dojde k opožděné inicializaci, použije se 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 třídy Lazy<T>, která používá konstruktor bez parametrů T a zadaný režim zabezpečení vlákna.

Lazy<T>(T)

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

Vlastnosti

IsValueCreated

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

Value

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

Metody

Equals(Object)

Určuje, zda je zadaný objekt roven aktuálnímu objektu.

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

Slouží jako výchozí funkce hash.

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

Získá Type aktuální instance.

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

Vytvoří mělkou kopii aktuálního Object.

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

Vytvoří a vrátí řetězcovou reprezentaci vlastnosti Value 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 třídy Lazy<T> bezpečnými vlákny a mohou být použity souběžně z více vláken. Tyto záruky zabezpečení vláken lze volitelně odebrat a pro každou instanci pomocí parametrů konstruktorů typu.

Viz také