Lazy<T> Classe

Définition

Prend en charge l'initialisation tardive.

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)

Paramètres de type

T

Type d’objet initialisé tardivement.

Héritage
Lazy<T>
Dérivé
Attributs

Exemples

L’exemple suivant illustre l’utilisation de la Lazy<T> classe pour fournir une initialisation différée avec accès à partir de plusieurs threads.

Notes

L’exemple utilise le Lazy<T>(Func<T>) constructeur. Il illustre également l’utilisation du Lazy<T>(Func<T>, Boolean) constructeur (spécifiant true pour isThreadSafe) et du Lazy<T>(Func<T>, LazyThreadSafetyMode) constructeur (spécifiant LazyThreadSafetyMode.ExecutionAndPublication pour mode). Pour basculer vers un autre constructeur, modifiez simplement les constructeurs qui sont commentés.

Pour obtenir un exemple illustrant la mise en cache des exceptions à l’aide des mêmes constructeurs, consultez le Lazy<T>(Func<T>) constructeur.

L'exemple définit une classe LargeObject qui sera initialisée tardivement par l'un des nombreux threads. Les quatre sections clés du code illustrent la création de l’initialiseur, la méthode d’usine, l’initialisation réelle et le constructeur de la LargeObject classe, qui affiche un message lorsque l’objet est créé. Au début de la méthode Main, l'exemple crée l'initialiseur de type lazy thread-safe pour 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)

La méthode de fabrique montre la création de l’objet, avec un espace réservé pour l’initialisation supplémentaire :

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

Notez que les deux premières sections de code peuvent être combinées à l’aide d’une fonction lambda, comme illustré ici :

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)

L’exemple suspend, pour indiquer qu’une période indéterminée peut s’écouler avant que l’initialisation différée ne se produise. Lorsque vous appuyez sur la touche Entrée , l’exemple crée et démarre trois threads. La ThreadProc méthode utilisée par les trois threads appelle la Value propriété. La première fois que cela se produit, l’instance LargeObject est créée :

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

Le constructeur de la LargeObject classe, qui inclut la dernière section clé du code, affiche un message et enregistre l’identité du thread d’initialisation. La sortie du programme s’affiche à la fin de la liste de codes complète.

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

Notes

Pour plus de simplicité, cet exemple utilise une instance globale de Lazy<T> et toutes les méthodes sont static (Shared en Visual Basic). Ce ne sont pas des exigences pour l’utilisation de l’initialisation tardive.

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
'

Remarques

Utilisez l’initialisation différée pour différer la création d’un objet volumineux ou gourmand en ressources, ou l’exécution d’une tâche gourmande en ressources, en particulier lorsque cette création ou exécution peut ne pas se produire pendant la durée de vie du programme.

Pour préparer l’initialisation paresseux, vous créez une instance de Lazy<T>. L’argument de type de l’objet Lazy<T> que vous créez spécifie le type de l’objet que vous souhaitez initialiser de manière différée. Le constructeur que vous utilisez pour créer l’objet Lazy<T> détermine les caractéristiques de l’initialisation. L’initialisation tardive se produit lors du premier accès à la propriété Lazy<T>.Value.

Dans la plupart des cas, le choix d’un constructeur dépend de vos réponses à deux questions :

  • L’objet initialisé sera-t-il accessible à partir de plusieurs threads ? Si tel est le cas, l’objet Lazy<T> peut le créer sur n’importe quel thread. Vous pouvez utiliser l’un des constructeurs simples dont le comportement par défaut consiste à créer un objet thread-safe Lazy<T> , de sorte qu’une seule instance de l’objet instancié parzily est créée peu importe le nombre de threads qui essaient de l’accéder. Pour créer un Lazy<T> objet qui n’est pas thread safe, vous devez utiliser un constructeur qui vous permet de spécifier aucune sécurité de thread.

    Attention

    Le fait que le Lazy<T> thread d’objet soit sécurisé ne protège pas l’objet initialisé par lezily. Si plusieurs threads peuvent accéder à l’objet initialisé de manière différée, vous devez sécuriser ses propriétés et ses méthodes pour l’accès multithread.

  • L’initialisation différée nécessite-t-elle un grand nombre de code, ou l’objet initialisé de manière différée dispose-t-il d’un constructeur sans paramètre qui fait tout ce dont vous avez besoin et ne lève pas d’exceptions ? Si vous devez écrire du code d’initialisation ou si des exceptions doivent être gérées, utilisez l’un des constructeurs qui acceptent une méthode de fabrique. Écrivez votre code d’initialisation dans la méthode d’usine.

Le tableau suivant montre le constructeur à choisir, en fonction de ces deux facteurs :

L’objet est accessible par Si aucun code d’initialisation n’est requis (constructeur sans paramètre), utilisez Si le code d’initialisation est requis, utilisez
Plusieurs threads Lazy<T>() Lazy<T>(Func<T>)
Un thread Lazy<T>(Boolean) avec isThreadSafe défini sur false. Lazy<T>(Func<T>, Boolean) avec isThreadSafe défini sur false.

Vous pouvez utiliser une expression lambda pour spécifier la méthode de fabrique. Cela conserve tout le code d’initialisation à un seul endroit. L’expression lambda capture le contexte, y compris tous les arguments que vous transmettez au constructeur de l’objet initialisé de manière différée.

Mise en cache d’exceptions Lorsque vous utilisez des méthodes de fabrique, les exceptions sont mises en cache. Autrement dit, si la méthode de fabrique lève une exception la première fois qu’un thread tente d’accéder à la Value propriété de l’objet Lazy<T> , la même exception est levée lors de chaque tentative suivante. Cela garantit que chaque appel à la Value propriété produit le même résultat et évite les erreurs subtiles qui peuvent se produire si différents threads obtiennent des résultats différents. Il Lazy<T> s’agit d’un réel T qui aurait sinon été initialisé à un moment donné, généralement pendant le démarrage. Une défaillance à ce stade antérieur est généralement irrécupérable. S’il existe un risque d’échec récupérable, nous vous recommandons de générer la logique de nouvelle tentative dans la routine d’initialisation (dans ce cas, la méthode d’usine), comme vous le feriez si vous n’utilisiez pas l’initialisation différée.

Alternative au verrouillage Dans certaines situations, vous pouvez éviter la surcharge du comportement de verrouillage par défaut de l’objet Lazy<T> . Dans de rares cas, il peut y avoir un risque d’interblocage. Dans ce cas, vous pouvez utiliser le constructeur ou Lazy<T>(Func<T>, LazyThreadSafetyMode) le Lazy<T>(LazyThreadSafetyMode) constructeur, et spécifier LazyThreadSafetyMode.PublicationOnly. Cela permet à l’objet Lazy<T> de créer une copie de l’objet initialisé de manière différée sur chacun de plusieurs threads si les threads appellent simultanément la Value propriété. L’objet Lazy<T> garantit que tous les threads utilisent la même instance de l’objet initialisé de manière différée et ignore les instances qui ne sont pas utilisées. Ainsi, le coût de réduction de la surcharge de verrouillage est que votre programme peut parfois créer et ignorer des copies supplémentaires d’un objet coûteux. Dans la plupart des cas, cela est peu probable. Les exemples pour les constructeurs et Lazy<T>(Func<T>, LazyThreadSafetyMode) les Lazy<T>(LazyThreadSafetyMode) constructeurs illustrent ce comportement.

Important

Lorsque vous spécifiez LazyThreadSafetyMode.PublicationOnly, les exceptions ne sont jamais mises en cache, même si vous spécifiez une méthode de fabrique.

Constructeurs équivalents Outre l’activation de l’utilisation, LazyThreadSafetyMode.PublicationOnlyles Lazy<T>(LazyThreadSafetyMode) constructeurs peuvent Lazy<T>(Func<T>, LazyThreadSafetyMode) dupliquer les fonctionnalités des autres constructeurs. Le tableau suivant montre les valeurs de paramètre qui produisent un comportement équivalent.

Pour créer un Lazy<T> objet qui est Pour les constructeurs qui ont un LazyThreadSafetyMode mode paramètre, défini sur mode Pour les constructeurs qui ont un paramètre booléenisThreadSafe, défini sur isThreadSafe Pour les constructeurs sans paramètres de sécurité de thread
Thread sécurisé entièrement ; utilise le verrouillage pour s’assurer qu’un seul thread initialise la valeur. ExecutionAndPublication true Tous ces constructeurs sont entièrement thread sécurisés.
Pas de thread sécurisé. None false Non applicable.
Thread sécurisé entièrement ; threads course pour initialiser la valeur. PublicationOnly Non applicable. Non applicable.

Autres fonctionnalités Pour plus d’informations sur l’utilisation de Lazy<T> champs statiques de thread ou comme magasin de stockage pour les propriétés, consultez Initialisation différée.

Constructeurs

Lazy<T>()

Initialise une nouvelle instance de la classe Lazy<T>. Lorsque l’initialisation tardive se produit, le constructeur sans paramètre du type cible est utilisé.

Lazy<T>(Boolean)

Initialise une nouvelle instance de la classe Lazy<T>. Lorsque l’initialisation tardive se produit, le constructeur sans paramètre du type cible et le mode d’initialisation spécifié sont utilisés.

Lazy<T>(Func<T>)

Initialise une nouvelle instance de la classe Lazy<T>. Quand l’initialisation tardive se produit, la fonction d’initialisation spécifiée est utilisée.

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

Initialise une nouvelle instance de la classe Lazy<T>. Quand l’initialisation tardive se produit, la fonction d’initialisation spécifiée et le mode d’initialisation sont utilisés.

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

Initialise une nouvelle instance de la classe Lazy<T> qui utilise la fonction d’initialisation et le mode de cohérence de thread spécifiés.

Lazy<T>(LazyThreadSafetyMode)

Initialise une nouvelle instance de la classe Lazy<T> qui utilise le constructeur sans paramètre de T et le mode de cohérence de thread spécifié.

Lazy<T>(T)

Initialise une nouvelle instance de la classe Lazy<T> qui utilise une valeur préinitialisée spécifiée.

Propriétés

IsValueCreated

Obtient une valeur qui indique si une valeur a été créée pour cette instance de Lazy<T>.

Value

Obtient la valeur initialisée tardivement de l’instance de Lazy<T> active.

Méthodes

Equals(Object)

Détermine si l'objet spécifié est égal à l'objet actuel.

(Hérité de Object)
GetHashCode()

Fait office de fonction de hachage par défaut.

(Hérité de Object)
GetType()

Obtient le Type de l'instance actuelle.

(Hérité de Object)
MemberwiseClone()

Crée une copie superficielle du Object actuel.

(Hérité de Object)
ToString()

Crée et retourne une représentation sous forme de chaîne de la propriété Value pour cette instance.

S’applique à

Cohérence de thread

Par défaut, tous les membres publics et protégés de la Lazy<T> classe sont thread sécurisés et peuvent être utilisés simultanément à partir de plusieurs threads. Ces garanties de sécurité des threads peuvent être supprimées éventuellement et par instance, en utilisant des paramètres aux constructeurs du type.

Voir aussi