Inisialisasi malas di C #

Inisialisasi malas, atau inisialisasi "malas", adalah cara untuk mengakses objek, bersembunyi di balik mekanisme yang memungkinkan Anda menunda pembuatan objek ini hingga saat pertama kali diakses. Perlunya inisialisasi malas dapat muncul karena berbagai alasan: dari keinginan untuk mengurangi beban saat memulai aplikasi hingga pengoptimalan fungsionalitas yang jarang digunakan. Memang tidak semua fungsi aplikasi selalu digunakan dan, terlebih lagi, segera, oleh karena itu, cukup rasional untuk menunda pembuatan objek yang mengimplementasikannya hingga waktu yang lebih baik. Saya ingin meninjau opsi inisialisasi malas yang tersedia dalam bahasa C #.



Untuk mendemonstrasikan contoh, saya akan menggunakan kelas Test, yang memiliki properti BlobData yang mengembalikan objek bertipe Blob, yang, menurut legenda, dibuat agak lambat, dan diputuskan untuk membuatnya dengan malas.



class Test
{
    public Blob BlobData
    {
        get
        {
            return new Blob();
        }
    }
}




Memeriksa null



Opsi paling sederhana, tersedia dari versi pertama bahasa, adalah membuat variabel yang tidak diinisialisasi dan mengujinya untuk null sebelum kembali. Jika variabelnya null, buat objek dan tetapkan ke variabel ini, lalu kembalikan. Pada akses berulang, objek tersebut sudah dibuat dan kami akan segera mengembalikannya.



class Test
{
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            if (_blob == null)
            {
                _blob = new Blob();
            }

            return _blob;
        }
    }
}




Objek tipe Blob dibuat di sini saat properti pertama kali diakses. Atau tidak dibuat jika karena alasan tertentu program tidak membutuhkannya dalam sesi ini.



Operator terner ?:



C # memiliki operator terner yang memungkinkan Anda menguji kondisi dan, jika benar, mengembalikan satu nilai, dan jika salah, nilai lain. Kita dapat menggunakannya untuk mempersingkat dan sedikit menyederhanakan kode.



class Test
{
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            return _blob == null
                ? _blob = new Blob()
                : _blob;
        }
    }
}




Intinya tetap sama. Jika objek tidak diinisialisasi, maka inisialisasi dan kembali. Jika sudah diinisialisasi, maka langsung saja kita kembalikan.



adalah nol



Situasinya berbeda dan kita, misalnya, mungkin menjumpai salah satu di mana kelas Blob memiliki operator == yang kelebihan beban. Untuk melakukan ini, kita mungkin perlu melakukan pemeriksaan is null daripada == null. Tersedia dalam versi bahasa terbaru.



return _blob is null
    ? _blob = new Blob()
    : _blob;




Tapi ini benar, penyimpangan kecil.



Operator penggabungan nol ??



Operator biner akan membantu kita untuk lebih menyederhanakan kode.

Inti dari karyanya adalah sebagai berikut. Jika operan pertama bukan null, maka akan dikembalikan. Jika operan pertama nol, operan kedua dikembalikan.



class Test
{
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            return _blob ?? (_blob = new Blob());
        }
    }
}




Operan kedua harus diapit tanda kurung karena prioritas operasi.



Operator ?? =



C # 8 memperkenalkan operator tugas penggabungan-nol yang terlihat seperti ini ?? =

Cara kerjanya adalah sebagai berikut. Jika operan pertama bukan null, maka itu akan dikembalikan. Jika operan pertama adalah null, maka nilai dari operan kedua ditetapkan padanya dan nilai ini dikembalikan.



class Test
{
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            return _blob ??= new Blob();
        }
    }
}




Ini memungkinkan kami untuk lebih mempersingkat kode.



Aliran



Jika ada kemungkinan bahwa beberapa utas dapat mengakses sumber daya tertentu sekaligus, kita harus membuatnya aman untuk utas. Jika tidak, situasi dapat terjadi, misalnya, kedua utas akan memeriksa objek untuk null, hasilnya akan salah, dan kemudian dua objek Blob akan dibuat, memuat sistem dua kali lebih banyak dari yang kita inginkan, dan sebagai tambahan, salah satunya benda akan disimpan, dan yang kedua akan hilang.



class Test
{
    private readonly object _lock = new object();
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            lock (_lock)
            {
                return _blob ?? (_blob = new Blob());
            }
        }
    }
}




Pernyataan kunci memperoleh kunci yang saling eksklusif pada objek yang ditentukan sebelum menjalankan pernyataan tertentu, dan kemudian melepaskan kunci tersebut. Ini setara dengan menggunakan System.Threading.Monitor.Enter (..., ...);



<T> malas



.NET 4.0 memperkenalkan kelas Lazy untuk menyembunyikan semua pekerjaan kotor ini dari mata kita. Sekarang kita hanya dapat meninggalkan variabel lokal tipe Lazy. Saat mengakses properti Value-nya, kita mendapatkan objek dari kelas Blob. Jika objek dibuat sebelumnya, maka akan langsung dikembalikan, jika tidak, akan dibuat terlebih dahulu.



class Test
{
    private readonly Lazy<Blob> _lazy = new Lazy<Blob>();

    public Blob BlobData
    {
        get
        {
            return _lazy.Value;
        }
    }
}




Karena kelas Blob memiliki konstruktor tanpa parameter, Lazy dapat membuatnya pada waktu yang tepat tanpa pertanyaan. Jika kita perlu melakukan beberapa tindakan tambahan selama pembuatan objek Blob, konstruktor kelas Lazy dapat merujuk ke Func <T>



private Lazy<Blob> _lazy = new Lazy<Blob>(() => new Blob());




Selain itu, pada parameter kedua dari konstruktor, kita dapat menunjukkan apakah kita membutuhkan pengaman benang (kunci yang sama).



Properti



Sekarang mari kita persingkat notasi properti readonly, karena C # modern memungkinkan Anda melakukan ini dengan baik. Pada akhirnya, semuanya terlihat seperti ini:



class Test
{
    private readonly Lazy<Blob> _lazy = new Lazy<Blob>();
    public Blob BlobData => _lazy.Value;
}




LazyInitializer



Ada juga opsi untuk tidak menggabungkan kelas dalam pembungkus Lazy, tetapi menggunakan fungsionalitas LazyInitializer. Kelas ini memiliki satu metode statis EnsureInitialized dengan sekumpulan kelebihan beban yang memungkinkan Anda membuat apa pun, termasuk keamanan thread dan menulis kode kustom untuk membuat objek, tetapi esensi utamanya adalah sebagai berikut. Periksa apakah objek belum diinisialisasi. Jika tidak, maka lakukan inisialisasi. Kembalikan sebuah objek. Dengan menggunakan kelas ini, kita dapat menulis ulang kode kita seperti ini:



class Test
{
    private Blob _blob;
    public Blob BlobData => LazyInitializer.EnsureInitialized(ref _blob);
}




Itu saja. Terima kasih atas perhatian Anda.



All Articles