Slabý vzor událostí

V aplikacích je možné, že obslužné rutiny připojené ke zdrojům událostí nebudou zničeny v koordinaci s objektem naslouchacího procesu, který připojil obslužnou rutinu ke zdroji. Tato situace může vést k nevracení paměti. Windows Presentation Foundation (WPF) zavádí vzor návrhu, který lze použít k vyřešení tohoto problému tím, že poskytuje vyhrazenou třídu správce pro konkrétní události a implementaci rozhraní naslouchací procesy pro danou událost. Tento vzor návrhu se označuje jako slabý vzor události.

Proč implementovat slabý vzor událostí?

Naslouchání událostem může vést k nevracení paměti. Typickou technikou naslouchání události je použití syntaxe specifické pro jazyk, která připojí obslužnou rutinu k události ve zdroji. Například v jazyce C# je tato syntaxe: source.SomeEvent += new SomeEventHandler(MyEventHandler).

Tato technika vytvoří silný odkaz ze zdroje událostí na naslouchací proces události. Obvykle připojení obslužné rutiny události pro naslouchací proces způsobí, že naslouchací proces bude mít životnost objektu, která je ovlivněna životností objektu zdroje (pokud obslužná rutina události není explicitně odebrána). Za určitých okolností ale můžete chtít, aby životnost naslouchacího procesu byla řízena jinými faktory, jako je například to, jestli aktuálně patří do vizuálního stromu aplikace, a ne životností zdroje. Vždy, když životnost zdrojového objektu přesahuje dobu života objektu naslouchacího procesu, normální vzor událostí vede k nevrácení paměti: naslouchací proces zůstane aktivní déle, než je zamýšleno.

Slabý vzor událostí je navržen tak, aby vyřešil tento problém nevracení paměti. Vzor slabých událostí lze použít vždy, když naslouchací proces potřebuje zaregistrovat událost, ale naslouchací proces explicitně neví, kdy se má zrušit registrace. Slabý vzor událostí lze použít také pokaždé, když životnost objektu zdroje překročí užitečnou životnost objektu naslouchacího procesu. (V tomto případě je užitečné určit vás.) Model slabých událostí umožňuje naslouchacímu procesu zaregistrovat a přijmout událost, aniž by to ovlivnilo vlastnosti životnosti objektu naslouchacího procesu jakýmkoli způsobem. Implicitní odkaz ze zdroje neurčuje, zda je naslouchací proces způsobilý pro uvolňování paměti. Odkaz je slabý odkaz, takže pojmenování slabého vzoru událostí a souvisejících rozhraní API. Naslouchací proces může být uvolněný z paměti nebo jinak zničen a zdroj může pokračovat bez zachování neschválených odkazů obslužné rutiny na nyní zničený objekt.

Kdo by se měl implementovat model slabých událostí?

Implementace slabého vzoru událostí je zajímavá především pro autory ovládacích prvků. Jako autor ovládacího prvku jste z velké části zodpovědní za chování a omezení vašeho ovládacího prvku a dopad, který má na aplikace, do kterých se vkládá. To zahrnuje chování životnosti řídicího objektu, zejména zpracování popsaného problému nevracení paměti.

Některé scénáře se ze své podstaty hodí k použití slabého vzoru události. Jedním z takových scénářů je datová vazba. V datové vazbě je běžné, že zdrojový objekt je zcela nezávislý na objektu naslouchacího procesu, což je cíl vazby. Mnoho aspektů datové vazby WPF již má použitý slabý vzor událostí v tom, jak se události implementují.

Implementace vzoru slabých událostí

Existují tři způsoby implementace slabého vzoru událostí. Následující tabulka uvádí tři přístupy a uvádí některé pokyny, kdy byste je měli použít.

Přístup Kdy implementovat
Použití existující slabé třídy správce událostí Pokud má událost, kterou chcete přihlásit k odběru, odpovídající WeakEventManager, použijte existujícího slabého správce událostí. Seznam slabých správců událostí, které jsou součástí WPF, naleznete v hierarchii dědičnosti ve WeakEventManager třídě. Vzhledem k tomu, že zahrnuté slabé správce událostí jsou omezené, budete pravděpodobně muset zvolit jeden z dalších přístupů.
Použití obecné slabé třídy správce událostí Použijte obecný, WeakEventManager<TEventSource,TEventArgs> pokud existující WeakEventManager není k dispozici, chcete snadný způsob implementace a nemáte obavy o efektivitu. WeakEventManager<TEventSource,TEventArgs> Obecný typ je méně efektivní než existující nebo vlastní slabý správce událostí. Například obecná třída se více odrazí za účelem zjištění události vzhledem k názvu události. Kód pro registraci události pomocí obecného WeakEventManager<TEventSource,TEventArgs> kódu je také více podrobný než použití existující nebo vlastní WeakEventManager.
Vytvoření vlastní slabé třídy správce událostí Vytvořte vlastní, WeakEventManager pokud existující WeakEventManager není k dispozici a chcete nejlepší efektivitu. Použití vlastního WeakEventManager odběru události bude efektivnější, ale na začátku se vám účtují náklady na psaní dalšího kódu.
Použití správce slabých událostí třetí strany NuGet má několik slabých správců událostí a mnoho architektur WPF také podporuje vzor.

Následující části popisují, jak implementovat slabý vzor událostí. Pro účely této diskuze má událost, která se přihlásí k odběru, následující charakteristiky.

  • Název události je SomeEvent.

  • Událost je vyvolána EventSource třídou.

  • Obslužná rutina události má typ: SomeEventEventHandler (nebo EventHandler<SomeEventEventArgs>).

  • Událost předává parametr typu SomeEventEventArgs obslužným rutinům událostí.

Použití existující třídy Weak Event Manager

  1. Najděte existujícího slabého správce událostí.

    Seznam slabých správců událostí, které jsou součástí WPF, naleznete v hierarchii dědičnosti ve WeakEventManager třídě.

  2. Místo normálního připojení událostí použijte nového slabého správce událostí.

    Pokud například váš kód používá k přihlášení k odběru události následující vzor:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
    

    Změňte ho na následující vzor:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
    

    Podobně platí, že pokud váš kód používá následující vzor k odhlášení odběru události:

    source.SomeEvent -= new SomeEventEventHandler(OnSomeEvent);
    

    Změňte ho na následující vzor:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
    

Použití obecné slabé třídy Event Manageru

  1. Místo normálního připojení událostí použijte obecnou WeakEventManager<TEventSource,TEventArgs> třídu.

    Při registraci naslouchacích procesů událostí zadáte WeakEventManager<TEventSource,TEventArgs> zdroj události a EventArgs zadáte jako parametry typu do třídy a volání AddHandler , jak je znázorněno v následujícím kódu:

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

Vytvoření vlastní slabé třídy Event Manageru

  1. Zkopírujte do projektu následující šablonu třídy.

    Tato třída dědí z WeakEventManager třídy.

    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 Nahraďte název vlastním názvem.

  3. Nahraďte tři názvy popsané dříve odpovídajícími názvy vaší události. (SomeEvent, EventSourcea SomeEventEventArgs)

  4. Nastavte viditelnost (veřejné / interní / soukromé) slabé třídy správce událostí na stejnou viditelnost jako událost, která spravuje.

  5. Místo normálního připojení událostí použijte nového slabého správce událostí.

    Pokud například váš kód používá k přihlášení k odběru události následující vzor:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
    

    Změňte ho na následující vzor:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
    

    Podobně platí, že pokud váš kód používá následující vzor k odhlášení odběru události:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);
    

    Změňte ho na následující vzor:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
    

Viz také