Lazy<T> Clase

Definición

Proporciona compatibilidad para la inicialización diferida.

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)

Parámetros de tipo

T

Tipo de objeto que se inicializa de forma diferida.

Herencia
Lazy<T>
Derivado
Atributos

Ejemplos

En el ejemplo siguiente se muestra el uso de la Lazy<T> clase para proporcionar inicialización diferida con acceso desde varios subprocesos.

Nota

En el ejemplo se usa el Lazy<T>(Func<T>) constructor . También muestra el uso del Lazy<T>(Func<T>, Boolean) constructor (especificando true para isThreadSafe) y el Lazy<T>(Func<T>, LazyThreadSafetyMode) constructor (especificando LazyThreadSafetyMode.ExecutionAndPublication para mode). Para cambiar a otro constructor, solo tiene que cambiar a qué constructores se comentan.

Para ver un ejemplo que muestra el almacenamiento en caché de excepciones mediante los mismos constructores, consulte el Lazy<T>(Func<T>) constructor .

El ejemplo define una clase LargeObject que uno de varios subprocesos inicializará de forma diferida. Las cuatro secciones clave del código muestran la creación del inicializador, el método de fábrica, la inicialización real y el constructor de la LargeObject clase , que muestra un mensaje cuando se crea el objeto. Al principio del método Main, el ejemplo crea el inicializador diferido seguro para subprocesos para 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)

El método factory muestra la creación del objeto, con un marcador de posición para una inicialización adicional:

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

Tenga en cuenta que las dos primeras secciones de código se pueden combinar mediante una función lambda, como se muestra aquí:

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)

El ejemplo se detiene para indicar que un período indeterminado puede transcurrir antes de que se produzca la inicialización diferida. Al presionar la tecla Entrar , el ejemplo crea e inicia tres subprocesos. El ThreadProc método que usan los tres subprocesos llama a la Value propiedad . La primera vez que esto sucede, se crea la LargeObject instancia:

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

El constructor de la LargeObject clase , que incluye la última sección de clave del código, muestra un mensaje y registra la identidad del subproceso de inicialización. La salida del programa aparece al final de la lista de código completa.

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

Nota

Por simplificar, en este ejemplo se emplea una instancia global de Lazy<T> y todos los métodos son static (Shared en Visual Basic). No son requisitos para el uso de la inicialización diferida.

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
'

Comentarios

Use la inicialización diferida para aplazar la creación de un objeto grande o intensivo de recursos, o la ejecución de una tarea que consume muchos recursos, especialmente cuando dicha creación o ejecución no se produzca durante la vigencia del programa.

Para prepararse para la inicialización diferida, cree una instancia de Lazy<T>. El argumento type del Lazy<T> objeto que se crea especifica el tipo del objeto que desea inicializar de forma diferiva. El constructor que se usa para crear el Lazy<T> objeto determina las características de la inicialización. La inicialización diferida se produce la primera vez que se accede a la propiedad Lazy<T>.Value.

En la mayoría de los casos, elegir un constructor depende de las respuestas a dos preguntas:

  • ¿Se accederá al objeto inicializado de forma diferida desde más de un subproceso? Si es así, el Lazy<T> objeto puede crearlo en cualquier subproceso. Puede usar uno de los constructores simples cuyo comportamiento predeterminado es crear un objeto seguro Lazy<T> para subprocesos, de modo que solo se cree una instancia del objeto de creación de instancias diferido independientemente del número de subprocesos que intenten acceder a él. Para crear un Lazy<T> objeto que no es seguro para subprocesos, debe usar un constructor que le permita especificar ninguna seguridad para subprocesos.

    Precaución

    Hacer que el Lazy<T> subproceso de objeto sea seguro no protege el objeto inicializado de forma difería. Si varios subprocesos pueden acceder al objeto inicializado de forma diferida, debe hacer que sus propiedades y métodos sean seguros para el acceso multiproceso.

  • ¿La inicialización diferida requiere una gran cantidad de código o el objeto inicializado de forma diferida tiene un constructor sin parámetros que hace todo lo que necesita y no produce excepciones? Si necesita escribir código de inicialización o si es necesario controlar las excepciones, use uno de los constructores que toma un método de generador. Escriba el código de inicialización en el método factory.

En la tabla siguiente se muestra qué constructor elegir, en función de estos dos factores:

Se accederá al objeto mediante Si no se requiere ningún código de inicialización (constructor sin parámetros), use Si se requiere código de inicialización, use
Varios subprocesos Lazy<T>() Lazy<T>(Func<T>)
Un subproceso Lazy<T>(Boolean) con isThreadSafe establecido en false. Lazy<T>(Func<T>, Boolean) con isThreadSafe establecido en false.

Puede usar una expresión lambda para especificar el método factory. Esto mantiene todo el código de inicialización en un solo lugar. La expresión lambda captura el contexto, incluidos los argumentos que se pasan al constructor del objeto inicializado de forma difería.

Almacenamiento en caché de excepciones Cuando se usan métodos de fábrica, se almacenan en caché las excepciones. Es decir, si el método de fábrica produce una excepción la primera vez que un subproceso intenta tener acceso a la Value propiedad del Lazy<T> objeto, se produce la misma excepción en cada intento posterior. Esto garantiza que cada llamada a la Value propiedad genera el mismo resultado y evita errores sutiles que pueden surgir si los subprocesos diferentes obtienen resultados diferentes. el Lazy<T> elemento representa un real T que, de lo contrario, se habría inicializado en algún momento anterior, normalmente durante el inicio. Un error en ese punto anterior suele ser irrecuperable. Si existe la posibilidad de que se produzca un error recuperable, se recomienda compilar la lógica de reintento en la rutina de inicialización (en este caso, el método factory), igual que lo haría si no usara la inicialización diferida.

Alternativa al bloqueo En determinadas situaciones, es posible que desee evitar la sobrecarga del Lazy<T> comportamiento de bloqueo predeterminado del objeto. En raras situaciones, puede haber un potencial para interbloqueos. En tales casos, puede usar el Lazy<T>(LazyThreadSafetyMode) constructor o Lazy<T>(Func<T>, LazyThreadSafetyMode) y especificar LazyThreadSafetyMode.PublicationOnly. Esto permite al Lazy<T> objeto crear una copia del objeto inicializado de forma diferida en cada uno de los subprocesos si los subprocesos llaman a la Value propiedad simultáneamente. El Lazy<T> objeto garantiza que todos los subprocesos usen la misma instancia del objeto inicializado de forma difería y descartan las instancias que no se usan. Por lo tanto, el costo de reducir la sobrecarga de bloqueo es que el programa a veces podría crear y descartar copias adicionales de un objeto costoso. En la mayoría de los casos, esto es poco probable. Los ejemplos de los Lazy<T>(LazyThreadSafetyMode) constructores y Lazy<T>(Func<T>, LazyThreadSafetyMode) muestran este comportamiento.

Importante

Cuando se especifica LazyThreadSafetyMode.PublicationOnly, las excepciones nunca se almacenan en caché, incluso si se especifica un método de fábrica.

Constructores equivalentes Además de habilitar el uso de LazyThreadSafetyMode.PublicationOnly, los Lazy<T>(LazyThreadSafetyMode) constructores y Lazy<T>(Func<T>, LazyThreadSafetyMode) pueden duplicar la funcionalidad de los otros constructores. En la tabla siguiente se muestran los valores de parámetro que producen un comportamiento equivalente.

Para crear un Lazy<T> objeto que sea En el caso de los constructores que tienen un LazyThreadSafetyMode mode parámetro , establezca en mode Para los constructores que tienen un parámetro booleanoisThreadSafe, se establece en isThreadSafe Para constructores sin parámetros de seguridad de subprocesos
Totalmente seguro para subprocesos; usa el bloqueo para asegurarse de que solo un subproceso inicializa el valor. ExecutionAndPublication true Todos estos constructores son totalmente seguros para subprocesos.
No es seguro para subprocesos. None false No es aplicable.
Totalmente seguro para subprocesos; los subprocesos compiten por inicializar el valor. PublicationOnly No es aplicable. No es aplicable.

Otras funcionalidades Para obtener información sobre el uso de Lazy<T> con campos estáticos de subprocesos o como almacén de respaldo para propiedades, vea Inicialización diferida.

Constructores

Lazy<T>()

Inicializa una nueva instancia de la clase Lazy<T>. Cuando se produce la inicialización diferida, se usa el constructor sin parámetros del tipo de destino.

Lazy<T>(Boolean)

Inicializa una nueva instancia de la clase Lazy<T>. Cuando se produce la inicialización diferida, se usan el constructor sin parámetros del tipo de destino y el modo de inicialización especificado.

Lazy<T>(Func<T>)

Inicializa una nueva instancia de la clase Lazy<T>. Cuando se produce la inicialización diferida, se usa la función de inicialización especificada.

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

Inicializa una nueva instancia de la clase Lazy<T>. Cuando se produce la inicialización diferida, se usan la función de inicialización y el modo de inicialización especificados.

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

Inicializa una nueva instancia de la clase Lazy<T> que usa la función de inicialización especificada y el modo de seguridad para subprocesos.

Lazy<T>(LazyThreadSafetyMode)

Inicializa una nueva instancia de la clase Lazy<T> que usa el constructor sin parámetros de T y el modo de seguridad para subprocesos especificado.

Lazy<T>(T)

Inicializa una nueva instancia de la clase Lazy<T> que usa un valor especificado preinicializado.

Propiedades

IsValueCreated

Obtiene un valor que indica si se se creó un valor para esta instancia Lazy<T>.

Value

Obtiene el valor inicializado de forma diferida de la instancia actual de Lazy<T>.

Métodos

Equals(Object)

Determina si el objeto especificado es igual que el objeto actual.

(Heredado de Object)
GetHashCode()

Sirve como la función hash predeterminada.

(Heredado de Object)
GetType()

Obtiene el Type de la instancia actual.

(Heredado de Object)
MemberwiseClone()

Crea una copia superficial del Object actual.

(Heredado de Object)
ToString()

Crea y devuelve una representación de cadena de la propiedad Value de esta instancia.

Se aplica a

Seguridad para subprocesos

De forma predeterminada, todos los miembros públicos y protegidos de la Lazy<T> clase son seguros para subprocesos y se pueden usar simultáneamente desde varios subprocesos. Estas garantías de seguridad para subprocesos se pueden quitar opcionalmente y por instancia, mediante parámetros para los constructores del tipo.

Consulte también