Singleton oleh klarifikasi Jon Skeet

214
public sealed class Singleton
{
    Singleton() {}

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested() {}
        internal static readonly Singleton instance = new Singleton();
    }
}

Saya ingin menerapkan pola Singleton Jon Skeet dalam aplikasi saya saat ini di C #.

Saya memiliki dua keraguan pada kode tersebut

  1. Bagaimana mungkin mengakses kelas luar di dalam kelas bersarang? maksudku

    internal static readonly Singleton instance = new Singleton();

    Apakah sesuatu yang disebut penutupan?

  2. Saya tidak dapat memahami komentar ini

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    

    apa komentar ini menyarankan kita?

amutha
sumber
12
haha saya pikir saya telah mengatakan bahwa itu agak khawatir lol ... ternyata menjadi John Nolan yang berbeda
John Antony Daniel Nolan
14
Dua hal diterima secara universal: matahari terbit dari timur dan Jon Skeet selalu benar. Tapi saya belum yakin tentang yang pertama: P
akhil_mittal
2
@ thepirat000 - Jika dia hanya seorang peserta di SO / Meta, saya mungkin tidak setuju, tetapi dia memiliki cukup pengaruh dalam dunia pemrograman yang sebenarnya yang mungkin benar-benar sah - saya yakin seseorang telah membuatnya pada satu titik atau yang lain .
Code Jockey
8
Taksonomi pertanyaan ini sedang dibahas dalam meta .
BoltClock

Jawaban:

359
  1. Tidak, ini tidak ada hubungannya dengan penutupan. Kelas bersarang memiliki akses ke anggota pribadi kelas luarnya, termasuk konstruktor pribadi di sini.

  2. Baca artikel saya di beforefieldinit . Anda mungkin atau mungkin tidak ingin konstruktor statis no-op - itu tergantung pada jaminan kemalasan yang Anda butuhkan. Anda harus menyadari bahwa .NET 4 mengubah semantik inisialisasi tipe aktual (masih dalam spesifikasi, tetapi lebih malas dari sebelumnya).

Apakah Anda benar - benar membutuhkan pola ini? Anda yakin tidak bisa lolos dengan:

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();
    public static Singleton Instance { get { return instance; } }

    static Singleton() {}
    private Singleton() {}
}
Jon Skeet
sumber
12
@Anindya: Tidak, tidak apa-apa. Anda mungkin ingin mengirim JetBrains untuk mengeluh :)
Jon Skeet
2
@ JonSkeet, saya baru saja menyampaikan kekhawatiran kepada JetBrains tentang ini (# RSRP-274373). Mari kita lihat apa yang bisa mereka pikirkan. :)
Anindya Chatterjee
3
@Moon: Anda tidak. Singleton hidup selama AppDomain.
Jon Skeet
2
@ JonSkeet Ada alasan untuk tidak menggunakan Lazy<T>sehingga Anda tidak perlu mendeklarasikan konstruktor statis untuk BeforeFieldInitefek samping ajaib ?
Ed T
3
FieldBeforeInitberasal MahaBharatadariMicrosoft
Amit Kumar Ghosh
49

Mengenai pertanyaan (1): Jawaban dari Jon benar, karena ia secara implisit menandai kelas 'Bersarang' pribadi dengan tidak menjadikannya publik atau internal :-). Anda sebaiknya melakukannya secara eksplisit dengan menambahkan 'pribadi':

    private class Nested

Mengenai pertanyaan (2): pada dasarnya apa posting tentang beforeinitfield dan tipe inisialisasi memberitahu Anda adalah bahwa jika Anda tidak memiliki konstruktor statis, runtime dapat menginisialisasi kapan saja (tetapi sebelum Anda menggunakannya). Jika Anda memiliki konstruktor statis, kode Anda di konstruktor statis mungkin menginisialisasi bidang, yang berarti bahwa runtime hanya diperbolehkan untuk menginisialisasi bidang ketika Anda meminta jenis.

Jadi jika Anda tidak ingin runtime menginisialisasi bidang 'secara proaktif' sebelum Anda menggunakannya, tambahkan konstruktor statis.

Either way, jika Anda menerapkan lajang Anda juga ingin inisialisasi sebagai malas mungkin dan tidak ketika runtime berpikir itu harus menginisialisasi variabel Anda - atau Anda mungkin tidak peduli. Dari pertanyaan Anda, saya kira Anda menginginkannya selambat-lambatnya.

Itu membawa bertemu ke posting Jon tentang singleton , yang merupakan topik mendasar dari pertanyaan ini. Oh dan keraguannya :-)

Saya ingin menunjukkan bahwa singleton # 3-nya, yang dia tandai 'salah', sebenarnya benar (karena kunci secara otomatis menyiratkan penghalang memori saat keluar ). Itu juga harus lebih cepat daripada singleton # 2 ketika Anda menggunakan instance lebih dari sekali (yang kurang lebih merupakan poin dari singleton :-)). Jadi, jika Anda benar-benar membutuhkan implementasi tunggal yang malas, saya mungkin akan menggunakan yang pertama - untuk alasan sederhana bahwa (1) sangat jelas bagi semua orang yang membaca kode Anda apa yang sedang terjadi dan (2) Anda tahu apa yang akan terjadi dengan pengecualian.

Jika Anda bertanya-tanya: Saya tidak akan pernah menggunakan singleton # 6 karena dapat dengan mudah menyebabkan kebuntuan dan perilaku tak terduga dengan pengecualian. Untuk detailnya, lihat: mode penguncian lazy , khususnya ExecutionAndPublication.

atlaste
sumber
62
Regarding question (1): The answer from Jon is correct ...Jon Skeet selalu benar ....
Noctis
72
Poin tambahan untuk mencoba jawaban tentang pertanyaan Jon Skeet di mana Jon Skeet telah menjawab.
valdetero
8
@valdetero Hahaha. Ini ... hahaha +1
akinuri