Bagikan melalui


Lazy<T> Kelas

Definisi

Menyediakan dukungan untuk inisialisasi malas.

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)

Jenis parameter

T

Jenis objek yang sedang diinisialisasi dengan malas.

Warisan
Lazy<T>
Turunan
Atribut

Contoh

Contoh berikut menunjukkan penggunaan kelas Lazy<T> untuk menyediakan inisialisasi malas dengan akses dari beberapa utas.

Nota

Contoh menggunakan konstruktor Lazy<T>(Func<T>). Ini juga menunjukkan penggunaan konstruktor Lazy<T>(Func<T>, Boolean) (menentukan true untuk isThreadSafe) dan konstruktor Lazy<T>(Func<T>, LazyThreadSafetyMode) (menentukan LazyThreadSafetyMode.ExecutionAndPublication untuk mode). Untuk beralih ke konstruktor yang berbeda, cukup ubah konstruktor mana yang dikomentari.

Untuk contoh yang menunjukkan penembolokan pengecualian menggunakan konstruktor yang sama, lihat konstruktor Lazy<T>(Func<T>).

Contoh mendefinisikan kelas LargeObject yang akan diinisialisasi dengan malas oleh salah satu dari beberapa utas. Empat bagian kunci kode mengilustrasikan pembuatan penginisialisasi, metode pabrik, inisialisasi aktual, dan konstruktor kelas LargeObject, yang menampilkan pesan saat objek dibuat. Di awal metode Main, contoh membuat penginisialisasi malas aman utas untuk 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)

Metode pabrik menunjukkan pembuatan objek, dengan tempat penampung untuk inisialisasi lebih lanjut:

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

Perhatikan bahwa dua bagian kode pertama dapat dikombinasikan dengan menggunakan fungsi lambda, seperti yang ditunjukkan di sini:

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)

Contoh jeda, untuk menunjukkan bahwa periode yang tidak ditentukan dapat berlalu sebelum inisialisasi malas terjadi. Saat Anda menekan tombol Enter, contoh membuat dan memulai tiga utas. Metode ThreadProc yang digunakan oleh ketiga utas memanggil properti Value. Pertama kali ini terjadi, instans LargeObject dibuat:

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 kelas LargeObject, yang mencakup bagian kunci kode terakhir, menampilkan pesan dan merekam identitas utas inisialisasi. Output dari program muncul di akhir daftar kode lengkap.

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

Untuk kesederhanaan, contoh ini menggunakan instans global Lazy<T>, dan semua metode static (Shared di Visual Basic). Ini bukan persyaratan untuk penggunaan inisialisasi malas.

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
'

Keterangan

Gunakan inisialisasi malas untuk menunda pembuatan objek besar atau intensif sumber daya, atau eksekusi tugas intensif sumber daya, terutama ketika pembuatan atau eksekusi tersebut mungkin tidak terjadi selama masa pakai program.

Untuk mempersiapkan inisialisasi malas, Anda membuat instans Lazy<T>. Argumen jenis objek Lazy<T> yang Anda buat menentukan jenis objek yang ingin Anda inisialisasi dengan malas. Konstruktor yang Anda gunakan untuk membuat objek Lazy<T> menentukan karakteristik inisialisasi. Inisialisasi malas terjadi saat pertama kali properti Lazy<T>.Value diakses.

Dalam kebanyakan kasus, memilih konstruktor tergantung pada jawaban Anda untuk dua pertanyaan:

  • Apakah objek yang diinisialisasi dengan malas akan diakses dari lebih dari satu utas? Jika demikian, objek Lazy<T> mungkin membuatnya di utas apa pun. Anda dapat menggunakan salah satu konstruktor sederhana yang perilaku defaultnya adalah membuat objek Lazy<T> aman utas, sehingga hanya satu instans objek yang dibuat dengan cepat tidak peduli berapa banyak utas yang mencoba mengaksesnya. Untuk membuat objek Lazy<T> yang tidak aman utas, Anda harus menggunakan konstruktor yang memungkinkan Anda menentukan tidak ada keamanan utas.

    Hati

    Membuat utas objek Lazy<T> aman tidak melindungi objek yang diinisialisasi dengan malas. Jika beberapa utas dapat mengakses objek yang diinisialisasi dengan malas, Anda harus membuat properti dan metodenya aman untuk akses multithreaded.

  • Apakah inisialisasi malas memerlukan banyak kode, atau apakah objek yang diinisialisasi dengan malas memiliki konstruktor tanpa parameter yang melakukan semua yang Anda butuhkan dan tidak melemparkan pengecualian? Jika Anda perlu menulis kode inisialisasi atau jika pengecualian perlu ditangani, gunakan salah satu konstruktor yang mengambil metode pabrik. Tulis kode inisialisasi Anda di metode pabrik.

Tabel berikut ini memperlihatkan konstruktor mana yang akan dipilih, berdasarkan dua faktor ini:

Objek akan diakses oleh Jika tidak ada kode inisialisasi yang diperlukan (konstruktor tanpa parameter), gunakan Jika kode inisialisasi diperlukan, gunakan
Beberapa utas Lazy<T>() Lazy<T>(Func<T>)
Satu utas Lazy<T>(Boolean) dengan isThreadSafe diatur ke false. Lazy<T>(Func<T>, Boolean) dengan isThreadSafe diatur ke false.

Anda dapat menggunakan ekspresi lambda untuk menentukan metode pabrik. Ini menyimpan semua kode inisialisasi di satu tempat. Ekspresi lambda menangkap konteks, termasuk argumen apa pun yang Anda teruskan ke konstruktor objek yang diinisialisasi dengan malas.

penembolokan pengecualian Saat Anda menggunakan metode pabrik, pengecualian di-cache. Artinya, jika metode pabrik melempar pengecualian saat pertama kali utas mencoba mengakses properti Value objek Lazy<T>, pengecualian yang sama dilemparkan pada setiap upaya berikutnya. Ini memastikan bahwa setiap panggilan ke properti Value menghasilkan hasil yang sama dan menghindari kesalahan halang yang mungkin muncul jika utas yang berbeda mendapatkan hasil yang berbeda. Lazy<T> berdiri untuk T aktual yang jika tidak akan diinisialisasi pada beberapa titik sebelumnya, biasanya selama startup. Kegagalan pada titik sebelumnya biasanya fatal. Jika ada potensi kegagalan yang dapat dipulihkan, kami sarankan Anda membangun logika coba lagi ke dalam rutinitas inisialisasi (dalam hal ini, metode pabrik), seperti yang Anda lakukan jika Anda tidak menggunakan inisialisasi malas.

Alternatif untuk mengunci Dalam situasi tertentu, Anda mungkin ingin menghindari overhead perilaku penguncian default objek Lazy<T>. Dalam situasi yang jarang terjadi, mungkin ada potensi kebuntuan. Dalam kasus seperti itu, Anda dapat menggunakan konstruktor Lazy<T>(LazyThreadSafetyMode) atau Lazy<T>(Func<T>, LazyThreadSafetyMode), dan menentukan LazyThreadSafetyMode.PublicationOnly. Ini memungkinkan objek Lazy<T> untuk membuat salinan objek yang diinisialisasi dengan malas pada masing-masing dari beberapa utas jika utas memanggil properti Value secara bersamaan. Objek Lazy<T> memastikan bahwa semua utas menggunakan instans yang sama dari objek yang diinisialisasi dengan malas dan membuang instans yang tidak digunakan. Dengan demikian, biaya mengurangi overhead penguncian adalah bahwa program Anda terkadang dapat membuat dan membuang salinan tambahan dari objek yang mahal. Dalam kebanyakan kasus, ini tidak mungkin. Contoh untuk konstruktor Lazy<T>(LazyThreadSafetyMode) dan Lazy<T>(Func<T>, LazyThreadSafetyMode) menunjukkan perilaku ini.

Penting

Saat Anda menentukan LazyThreadSafetyMode.PublicationOnly, pengecualian tidak pernah di-cache, bahkan jika Anda menentukan metode pabrik.

konstruktor setara Selain mengaktifkan penggunaan konstruktor LazyThreadSafetyMode.PublicationOnly, konstruktor Lazy<T>(LazyThreadSafetyMode) dan Lazy<T>(Func<T>, LazyThreadSafetyMode) dapat menduplikasi fungsionalitas konstruktor lain. Tabel berikut ini memperlihatkan nilai parameter yang menghasilkan perilaku yang setara.

Untuk membuat objek Lazy<T> yang Untuk konstruktor yang memiliki parameter LazyThreadSafetyModemode, atur mode ke Untuk konstruktor yang memiliki parameter isThreadSafe Boolean, atur isThreadSafe ke Untuk konstruktor tanpa parameter keamanan utas
Sepenuhnya aman utas; menggunakan penguncian untuk memastikan bahwa hanya satu utas yang menginisialisasi nilai. ExecutionAndPublication true Semua konstruktor tersebut sepenuhnya aman utas.
Tidak aman utas. None false Tidak berlaku.
Sepenuhnya aman utas; utas berlomba untuk menginisialisasi nilai. PublicationOnly Tidak berlaku. Tidak berlaku.

Kemampuan lain Untuk informasi tentang penggunaan Lazy<T> dengan bidang thread-static, atau sebagai penyimpanan backing untuk properti, lihat Inisialisasi Malas.

Konstruktor

Lazy<T>()

Menginisialisasi instans baru kelas Lazy<T>. Ketika inisialisasi malas terjadi, konstruktor tanpa parameter dari jenis target digunakan.

Lazy<T>(Boolean)

Menginisialisasi instans baru kelas Lazy<T>. Ketika inisialisasi malas terjadi, konstruktor tanpa parameter dari jenis target dan mode inisialisasi yang ditentukan digunakan.

Lazy<T>(Func<T>)

Menginisialisasi instans baru kelas Lazy<T>. Ketika inisialisasi malas terjadi, fungsi inisialisasi yang ditentukan digunakan.

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

Menginisialisasi instans baru kelas Lazy<T>. Ketika inisialisasi malas terjadi, fungsi inisialisasi dan mode inisialisasi yang ditentukan digunakan.

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

Menginisialisasi instans baru kelas Lazy<T> yang menggunakan fungsi inisialisasi dan mode keamanan utas yang ditentukan.

Lazy<T>(LazyThreadSafetyMode)

Menginisialisasi instans baru kelas Lazy<T> yang menggunakan konstruktor tanpa parameter T dan mode keamanan utas yang ditentukan.

Lazy<T>(T)

Menginisialisasi instans baru kelas Lazy<T> yang menggunakan nilai yang ditentukan sebelumnya.

Properti

IsValueCreated

Mendapatkan nilai yang menunjukkan apakah nilai telah dibuat untuk instans Lazy<T> ini.

Value

Mendapatkan nilai yang diinisialisasi dengan malas dari instans Lazy<T> saat ini.

Metode

Equals(Object)

Menentukan apakah objek yang ditentukan sama dengan objek saat ini.

(Diperoleh dari Object)
GetHashCode()

Berfungsi sebagai fungsi hash default.

(Diperoleh dari Object)
GetType()

Mendapatkan Type instans saat ini.

(Diperoleh dari Object)
MemberwiseClone()

Membuat salinan dangkal dari Objectsaat ini.

(Diperoleh dari Object)
ToString()

Membuat dan mengembalikan representasi string dari properti Value untuk instans ini.

Berlaku untuk

Keamanan Thread

Secara default, semua anggota publik dan terlindungi dari kelas Lazy<T> aman utas dan dapat digunakan secara bersamaan dari beberapa utas. Jaminan keamanan utas ini dapat dihapus secara opsional dan per instans, menggunakan parameter untuk konstruktor jenis.

Lihat juga