Bagikan melalui


Pola Peristiwa Lemah

Dalam aplikasi, ada kemungkinan bahwa handler yang melekat pada sumber peristiwa tidak akan dihancurkan dalam koordinasi dengan objek pendengar yang melampirkan handler ke sumber. Situasi ini dapat menyebabkan kebocoran memori. Windows Presentation Foundation (WPF) memperkenalkan pola desain yang dapat digunakan untuk mengatasi masalah ini, dengan menyediakan kelas manajer khusus untuk peristiwa tertentu dan menerapkan antarmuka pada pendengar untuk peristiwa tersebut. Pola desain ini dikenal sebagai pola peristiwa yang lemah.

Mengapa Menerapkan Pola Peristiwa Lemah?

Mendengarkan peristiwa dapat menyebabkan kebocoran memori. Teknik khas untuk mendengarkan peristiwa adalah menggunakan sintaksis khusus bahasa yang melampirkan handler ke peristiwa di sumber. Misalnya, dalam C#, sintaks tersebut adalah: source.SomeEvent += new SomeEventHandler(MyEventHandler).

Teknik ini menciptakan referensi yang kuat dari sumber peristiwa ke pendengar peristiwa. Biasanya, melampirkan penanganan aktivitas untuk pendengar menyebabkan pendengar memiliki masa pakai objek yang dipengaruhi oleh masa pakai objek sumber (kecuali penanganan aktivitas dihapus secara eksplisit). Tetapi dalam keadaan tertentu, Anda mungkin ingin masa pakai objek pendengar dikontrol oleh faktor lain, seperti apakah saat ini milik pohon visual aplikasi, dan bukan oleh masa pakai sumber. Setiap kali masa pakai objek sumber meluas di luar masa pakai objek pendengar, pola peristiwa normal menyebabkan kebocoran memori: pendengar tetap hidup lebih lama dari yang dimaksudkan.

Pola peristiwa yang lemah dirancang untuk menyelesaikan masalah kebocoran memori ini. Pola peristiwa yang lemah dapat digunakan setiap kali pendengar perlu mendaftar untuk suatu peristiwa, tetapi pendengar tidak secara eksplisit tahu kapan harus membatalkan pendaftaran. Pola peristiwa yang lemah juga dapat digunakan setiap kali masa pakai objek sumber melebihi masa pakai objek yang berguna dari pendengar. (Dalam hal ini, berguna ditentukan oleh Anda.) Pola peristiwa yang lemah memungkinkan pendengar untuk mendaftar dan menerima peristiwa tanpa memengaruhi karakteristik masa pakai objek pendengar dengan cara apa pun. Akibatnya, referensi tersirat dari sumber tidak menentukan apakah pendengar memenuhi syarat untuk pengumpulan sampah. Referensi adalah referensi yang lemah, sehingga penamaan pola peristiwa yang lemah dan API terkait. Pendengar dapat menjadi sampah yang dikumpulkan atau dihancurkan, dan sumber dapat dilanjutkan tanpa mempertahankan referensi handler yang tidak dapat dikoleksi ke objek yang sekarang hancur.

Siapa Harus Menerapkan Pola Peristiwa Lemah?

Menerapkan pola peristiwa yang lemah sangat menarik terutama untuk penulis kontrol. Sebagai penulis kontrol, Anda sebagian besar bertanggung jawab atas perilaku dan penahanan kontrol Anda dan dampaknya pada aplikasi di mana ia dimasukkan. Ini termasuk perilaku seumur hidup objek kontrol, khususnya penanganan masalah kebocoran memori yang dijelaskan.

Skenario tertentu secara inheren meminjamkan diri mereka ke penerapan pola peristiwa yang lemah. Salah satu skenario tersebut adalah pengikatan data. Dalam pengikatan data, umum bagi objek sumber untuk sepenuhnya independen dari objek pendengar, yang merupakan target pengikatan. Banyak aspek pengikatan data WPF sudah memiliki pola peristiwa lemah yang diterapkan dalam bagaimana peristiwa diimplementasikan.

Cara Menerapkan Pola Peristiwa Lemah

Ada tiga cara untuk menerapkan pola peristiwa yang lemah. Tabel berikut mencantumkan tiga pendekatan dan memberikan beberapa panduan kapan Anda harus menggunakan masing-masing pendekatan.

Pendekatan Kapan harus Menerapkan
Menggunakan kelas manajer peristiwa lemah yang ada Jika peristiwa yang ingin Anda berlangganan memiliki yang sesuai WeakEventManager, gunakan pengelola peristiwa lemah yang ada. Untuk daftar manajer peristiwa lemah yang disertakan dengan WPF, lihat hierarki warisan di WeakEventManager kelas . Karena manajer peristiwa lemah yang disertakan terbatas, Anda mungkin perlu memilih salah satu pendekatan lainnya.
Menggunakan kelas manajer peristiwa lemah generik Gunakan generik WeakEventManager<TEventSource,TEventArgs> ketika yang ada WeakEventManager tidak tersedia, Anda menginginkan cara mudah untuk menerapkan, dan Anda tidak khawatir tentang efisiensi. Generik WeakEventManager<TEventSource,TEventArgs> kurang efisien daripada manajer peristiwa lemah yang ada atau kustom. Misalnya, kelas generik melakukan lebih banyak refleksi untuk menemukan peristiwa yang diberi nama peristiwa. Selain itu, kode untuk mendaftarkan peristiwa dengan menggunakan generik WeakEventManager<TEventSource,TEventArgs> lebih verbose daripada menggunakan yang ada atau kustom WeakEventManager.
Membuat kelas manajer peristiwa lemah kustom Buat kustom WeakEventManager saat yang ada WeakEventManager tidak tersedia dan Anda menginginkan efisiensi terbaik. Menggunakan kustom WeakEventManager untuk berlangganan peristiwa akan lebih efisien, tetapi Anda dikenakan biaya menulis lebih banyak kode di awal.
Menggunakan pengelola peristiwa lemah pihak ketiga NuGet memiliki beberapa manajer peristiwa yang lemah dan banyak kerangka kerja WPF juga mendukung pola tersebut.

Bagian berikut menjelaskan cara mengimplementasikan pola peristiwa yang lemah. Untuk tujuan diskusi ini, acara untuk berlangganan memiliki karakteristik berikut.

  • Nama peristiwa adalah SomeEvent.

  • Acara ini dinaikkan oleh EventSource kelas .

  • Penanganan aktivitas memiliki jenis: SomeEventEventHandler (atau EventHandler<SomeEventEventArgs>).

  • Peristiwa meneruskan parameter jenis SomeEventEventArgs ke penanganan aktivitas.

Menggunakan Kelas Manajer Peristiwa Lemah yang Ada

  1. Temukan manajer peristiwa lemah yang ada.

    Untuk daftar manajer peristiwa lemah yang disertakan dengan WPF, lihat hierarki warisan di WeakEventManager kelas .

  2. Gunakan manajer peristiwa lemah baru alih-alih hookup peristiwa normal.

    Misalnya, jika kode Anda menggunakan pola berikut untuk berlangganan peristiwa:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
    

    Ubah ke pola berikut:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
    

    Demikian pula, jika kode Anda menggunakan pola berikut untuk berhenti berlangganan dari peristiwa:

    source.SomeEvent -= new SomeEventEventHandler(OnSomeEvent);
    

    Ubah ke pola berikut:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
    

Menggunakan Kelas Manajer Peristiwa Lemah Generik

  1. Gunakan kelas generik WeakEventManager<TEventSource,TEventArgs> alih-alih hookup peristiwa normal.

    Saat Anda menggunakan WeakEventManager<TEventSource,TEventArgs> untuk mendaftarkan pendengar peristiwa, Anda menyediakan sumber peristiwa dan EventArgs mengetik sebagai parameter jenis ke kelas dan memanggil AddHandler seperti yang ditunjukkan dalam kode berikut:

    WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);
    

Membuat Kelas Manajer Peristiwa Lemah Kustom

  1. Salin templat kelas berikut ke proyek Anda.

    Kelas ini mewarisi dari WeakEventManager kelas .

    class SomeEventWeakEventManager : WeakEventManager
    {
    
        private SomeEventWeakEventManager()
        {
        }
    
        /// <summary>
        /// Add a handler for the given source's event.
        /// </summary>
        public static void AddHandler(EventSource source,
                                      EventHandler<SomeEventEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");
    
            CurrentManager.ProtectedAddHandler(source, handler);
        }
    
        /// <summary>
        /// Remove a handler for the given source's event.
        /// </summary>
        public static void RemoveHandler(EventSource source,
                                         EventHandler<SomeEventEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");
    
            CurrentManager.ProtectedRemoveHandler(source, handler);
        }
    
        /// <summary>
        /// Get the event manager for the current thread.
        /// </summary>
        private static SomeEventWeakEventManager CurrentManager
        {
            get
            {
                Type managerType = typeof(SomeEventWeakEventManager);
                SomeEventWeakEventManager manager =
                    (SomeEventWeakEventManager)GetCurrentManager(managerType);
    
                // at first use, create and register a new manager
                if (manager == null)
                {
                    manager = new SomeEventWeakEventManager();
                    SetCurrentManager(managerType, manager);
                }
    
                return manager;
            }
        }
    
        /// <summary>
        /// Return a new list to hold listeners to the event.
        /// </summary>
        protected override ListenerList NewListenerList()
        {
            return new ListenerList<SomeEventEventArgs>();
        }
    
        /// <summary>
        /// Listen to the given source for the event.
        /// </summary>
        protected override void StartListening(object source)
        {
            EventSource typedSource = (EventSource)source;
            typedSource.SomeEvent += new EventHandler<SomeEventEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Stop listening to the given source for the event.
        /// </summary>
        protected override void StopListening(object source)
        {
            EventSource typedSource = (EventSource)source;
            typedSource.SomeEvent -= new EventHandler<SomeEventEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Event handler for the SomeEvent event.
        /// </summary>
        void OnSomeEvent(object sender, SomeEventEventArgs e)
        {
            DeliverEvent(sender, e);
        }
    }
    
  2. SomeEventWeakEventManager Ganti nama dengan nama Anda sendiri.

  3. Ganti tiga nama yang dijelaskan sebelumnya dengan nama yang sesuai untuk peristiwa Anda. (SomeEvent, EventSource, dan SomeEventEventArgs)

  4. Atur visibilitas (publik/internal/privat) dari kelas pengelola peristiwa yang lemah ke visibilitas yang sama seperti yang dikelolanya.

  5. Gunakan manajer peristiwa lemah baru alih-alih hookup peristiwa normal.

    Misalnya, jika kode Anda menggunakan pola berikut untuk berlangganan peristiwa:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
    

    Ubah ke pola berikut:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
    

    Demikian pula, jika kode Anda menggunakan pola berikut untuk berhenti berlangganan peristiwa:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);
    

    Ubah ke pola berikut:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
    

Baca juga