Aracılığıyla paylaş


Zayıf olay desenleri (WPF .NET)

Uygulamalarda, olay kaynaklarına bağlı işleyiciler, işleyiciyi kaynağa ekleyen dinleyici nesnesiyle eşgüdümlü olarak yok edilmeyecektir. Bu durum bellek sızıntılarına neden olabilir. Windows Presentation Foundation (WPF), bu sorunu gidermek için kullanılabilecek bir tasarım deseni ekler. Tasarım düzeni belirli olaylar için ayrılmış bir yönetici sınıfı sağlar ve bu olay için dinleyicilere bir arabirim uygular. Bu tasarım deseni zayıf olay deseni olarak bilinir.

Önemli

.NET 7 ve .NET 6 için Masaüstü Kılavuzu belgeleri yapım aşamasındadır.

Önkoşullar

Makalede, yönlendirilen olaylar hakkında temel bilgiler edindiğiniz ve Yönlendirilen olaylara genel bakış makalesini okuduğunuz varsayılır. Bu makaledeki örnekleri takip etmek için, Genişletilebilir Uygulama biçimlendirme dili (XAML) hakkında bilgi sahibi olmanız ve Windows Presentation Foundation (WPF) uygulamalarının nasıl yazıldığından haberdar olmanız yardımcı olur.

Zayıf olay düzeni neden uygulansın?

Olayları dinlemek bellek sızıntılarına neden olabilir. Bir olayı dinlemenin olağan tekniği, bir kaynakta bir olaya işleyici eklemek için dile özgü söz dizimini kullanmaktır. Örneğin, C# deyimi source.SomeEvent += new SomeEventHandler(MyEventHandler) veya VB deyimi AddHandler source.SomeEvent, AddressOf MyEventHandler. Ancak bu teknik, olay kaynağından olay dinleyicisine güçlü bir başvuru oluşturur. Olay işleyicisi açıkça kaydedilmediği sürece, dinleyicinin nesne ömrü kaynağın nesne ömründen etkilenir. Belirli durumlarda, dinleyicinin nesne ömrünün, şu anda uygulamanın görsel ağacına ait olup olmadığı gibi diğer faktörler tarafından denetlenmesini isteyebilirsiniz. Kaynağın nesne ömrü dinleyicinin yararlı nesne ömrünü aşıyorsa, dinleyici gerekenden daha uzun süre canlı tutulur. Bu durumda ayrılmamış bellek bir bellek sızıntısına neden olur.

Zayıf olay düzeni, bellek sızıntısı sorununu çözmek için tasarlanmıştır. Zayıf olay düzeni, dinleyicinin bir olaya kaydolması gerektiğinde kullanılabilir, ancak dinleyici kaydın ne zaman silinebileceğini açıkça bilmez. Zayıf olay deseni, kaynağın nesne ömrü dinleyicinin yararlı nesne ömrünü aştığında da kullanılabilir. Bu durumda, yararlı sizin tarafınızdan belirlenir. Zayıf olay düzeni dinleyicinin nesne ömrü özelliklerini herhangi bir şekilde etkilemeden olayı kaydetmesine ve almasına olanak tanır. Sonuç olarak, kaynaktan gelen zımni başvuru dinleyicinin çöp toplama için uygun olup olmadığını belirlemez. Başvuru zayıf bir başvurudur, bu nedenle zayıf olay deseninin ve ilgili API'lerin adlandırılmasıdır. Dinleyici çöp olarak toplanabilir veya başka bir şekilde yok edilebilir ve kaynak, artık yok edilmiş bir nesneye yönelik akla alınamayan işleyici başvurularını korumadan devam edebilir.

Zayıf olay düzenini kim uygulamalı?

Zayıf olay düzeni öncelikli olarak denetim yazarları ile ilgilidir. Denetim yazarı olarak, denetiminizin davranışından ve kapsama alanından ve denetimin eklendiği uygulamalar üzerindeki etkisinden büyük ölçüde siz sorumlu olursunuz. Bu, denetimin nesne yaşam süresi davranışını, özellikle de açıklanan bellek sızıntısı sorununun işlenmesini içerir.

Bazı senaryolar doğal olarak zayıf olay deseninin uygulanmasına olanak sağlar. Bu tür senaryolardan biri veri bağlamadır. Veri bağlamada, kaynak nesnenin bir bağlamanın hedefi olan dinleyici nesnesinden bağımsız olması yaygın bir durumdur. WPF veri bağlamasının birçok yönü, olayların nasıl uygulandığında zaten zayıf olay desenini uygulamıştır.

Zayıf olay desenini uygulama

Zayıf olay desenini uygulamanın dört yolu vardır ve her yaklaşım farklı bir olay yöneticisi kullanır. Senaryonuza en uygun olay yöneticisini seçin.

  • Mevcut zayıf olay yöneticisi:

    Abone olmak istediğiniz olayın karşılık gelen WeakEventManagerbir öğesi olduğunda mevcut zayıf olay yöneticisi sınıfını kullanın. WPF'ye dahil edilen zayıf olay yöneticilerinin listesi için sınıftaki devralma hiyerarşisine WeakEventManager bakın. Dahil edilen zayıf olay yöneticileri sınırlı olduğundan, büyük olasılıkla diğer yaklaşımlardan birini seçmeniz gerekir.

  • Genel zayıf olay yöneticisi:

    Mevcut WeakEventManager bir mevcut olmadığında ve zayıf olayları uygulamanın en kolay yolunu arıyorsanız genel WeakEventManager<TEventSource,TEventArgs> kullanın. Ancak genel WeakEventManager<TEventSource,TEventArgs> , olayı adından bulmak için yansıma kullandığından mevcut veya özel zayıf olay yöneticisinden daha az verimlidir. Ayrıca, olayı genel WeakEventManager<TEventSource,TEventArgs> kullanarak kaydetmek için gereken kod, mevcut veya özel WeakEventManagerbir kullanmaktan daha ayrıntılıdır.

  • Özel zayıf olay yöneticisi:

    Mevcut bir mevcut WeakEventManager olmadığında özel WeakEventManager bir oluşturma ve verimlilik kritik önem taşır. Genel bir WeakEventManagerdeğerinden daha verimli olsa da, bir özel WeakEventManager daha önceden kod yazmanızı gerektirir.

  • Üçüncü taraf zayıf olay yöneticisi:

    Diğer yaklaşımlar tarafından sağlanmayan işlevlere ihtiyacınız olduğunda üçüncü taraf zayıf olay yöneticisi kullanın. NuGet'in bazı zayıf olay yöneticileri vardır. Birçok WPF çerçevesi de deseni destekler.

Aşağıdaki bölümlerde, farklı olay yöneticisi türlerini kullanarak zayıf olay deseninin nasıl uygulandığı açıklanmaktadır. Genel ve özel zayıf olay yöneticisi örnekleri için abone olunacak olay aşağıdaki özelliklere sahiptir.

  • Olay adı şeklindedir SomeEvent.
  • olay sınıfı tarafından SomeEventSource oluşturulur.
  • Olay işleyicisi türüne EventHandler<SomeEventArgs>sahiptir.
  • olay türündeki SomeEventArgs bir parametreyi olay işleyicilerine geçirir.

Mevcut zayıf olay yöneticisi sınıfını kullanma

  1. Mevcut zayıf olay yöneticisini bulun. WPF'ye dahil edilen zayıf olay yöneticilerinin listesi için sınıfının devralma hiyerarşisine WeakEventManager bakın.

  2. Normal olay bağlaması yerine yeni zayıf olay yöneticisini kullanın.

    Örneğin, kodunuz bir olaya abone olmak için aşağıdaki deseni kullanıyorsa:

    source.LostFocus += new RoutedEventHandler(Source_LostFocus);
    
    AddHandler source.LostFocus, New RoutedEventHandler(AddressOf Source_LostFocus)
    

    Bunu aşağıdaki desenle değiştirin:

    LostFocusEventManager.AddHandler(source, Source_LostFocus);
    
    LostFocusEventManager.AddHandler(
        source, New EventHandler(Of RoutedEventArgs)(AddressOf Source_LostFocus))
    

    Benzer şekilde, kodunuz bir olayın aboneliğini kaldırmak için aşağıdaki deseni kullanıyorsa:

    source.LostFocus -= new RoutedEventHandler(Source_LostFocus);
    
    RemoveHandler source.LostFocus, New RoutedEventHandler(AddressOf Source_LostFocus)
    

    Bunu aşağıdaki desenle değiştirin:

    LostFocusEventManager.RemoveHandler(source, Source_LostFocus);
    
    LostFocusEventManager.RemoveHandler(
        source, New EventHandler(Of RoutedEventArgs)(AddressOf Source_LostFocus))
    

Genel zayıf olay yöneticisi sınıfını kullanma

Normal olay bağlaması yerine genel WeakEventManager<TEventSource,TEventArgs> sınıfını kullanın.

Olay dinleyicilerini kaydetmek için kullandığınızda WeakEventManager<TEventSource,TEventArgs> , olay kaynağını sağlar ve EventArgs sınıfına tür parametreleri olarak yazarsınız. Aşağıdaki kodda gösterildiği gibi çağırın AddHandler :

WeakEventManager<SomeEventSource, SomeEventArgs>.AddHandler(source, "SomeEvent", Source_SomeEvent);
WeakEventManager(Of SomeEventSource, SomeEventArgs).AddHandler(
    source, "SomeEvent", New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent))

Özel zayıf olay yöneticisi sınıfı oluşturma

  1. Aşağıdaki sınıf şablonunu projenize kopyalayın. Aşağıdaki sınıf sınıfından devralır WeakEventManager :

    class SomeEventWeakEventManager : WeakEventManager
    {
        private SomeEventWeakEventManager()
        {
        }
    
        /// <summary>
        /// Add a handler for the given source's event.
        /// </summary>
        public static void AddHandler(SomeEventSource source,
                                      EventHandler<SomeEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (handler == null)
                throw new ArgumentNullException(nameof(handler));
    
            CurrentManager.ProtectedAddHandler(source, handler);
        }
    
        /// <summary>
        /// Remove a handler for the given source's event.
        /// </summary>
        public static void RemoveHandler(SomeEventSource source,
                                         EventHandler<SomeEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (handler == null)
                throw new ArgumentNullException(nameof(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<SomeEventArgs>();
        }
    
        /// <summary>
        /// Listen to the given source for the event.
        /// </summary>
        protected override void StartListening(object source)
        {
            SomeEventSource typedSource = (SomeEventSource)source;
            typedSource.SomeEvent += new EventHandler<SomeEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Stop listening to the given source for the event.
        /// </summary>
        protected override void StopListening(object source)
        {
            SomeEventSource typedSource = (SomeEventSource)source;
            typedSource.SomeEvent -= new EventHandler<SomeEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Event handler for the SomeEvent event.
        /// </summary>
        void OnSomeEvent(object sender, SomeEventArgs e)
        {
            DeliverEvent(sender, e);
        }
    }
    
    Class SomeEventWeakEventManager
        Inherits WeakEventManager
    
        Private Sub New()
        End Sub
    
        ''' <summary>
        ''' Add a handler for the given source's event.
        ''' </summary>
        Public Shared Sub [AddHandler](source As SomeEventSource,
                                       handler As EventHandler(Of SomeEventArgs))
            If source Is Nothing Then Throw New ArgumentNullException(NameOf(source))
            If handler Is Nothing Then Throw New ArgumentNullException(NameOf(handler))
            CurrentManager.ProtectedAddHandler(source, handler)
        End Sub
    
        ''' <summary>
        ''' Remove a handler for the given source's event.
        ''' </summary>
        Public Shared Sub [RemoveHandler](source As SomeEventSource,
                                          handler As EventHandler(Of SomeEventArgs))
            If source Is Nothing Then Throw New ArgumentNullException(NameOf(source))
            If handler Is Nothing Then Throw New ArgumentNullException(NameOf(handler))
            CurrentManager.ProtectedRemoveHandler(source, handler)
        End Sub
    
        ''' <summary>
        ''' Get the event manager for the current thread.
        ''' </summary>
        Private Shared ReadOnly Property CurrentManager As SomeEventWeakEventManager
            Get
                Dim managerType As Type = GetType(SomeEventWeakEventManager)
                Dim manager As SomeEventWeakEventManager =
                    CType(GetCurrentManager(managerType), SomeEventWeakEventManager)
    
                If manager Is Nothing Then
                    manager = New SomeEventWeakEventManager()
                    SetCurrentManager(managerType, manager)
                End If
    
                Return manager
            End Get
        End Property
    
        ''' <summary>
        ''' Return a new list to hold listeners to the event.
        ''' </summary>
        Protected Overrides Function NewListenerList() As ListenerList
            Return New ListenerList(Of SomeEventArgs)()
        End Function
    
        ''' <summary>
        ''' Listen to the given source for the event.
        ''' </summary>
        Protected Overrides Sub StartListening(source As Object)
            Dim typedSource As SomeEventSource = CType(source, SomeEventSource)
            AddHandler typedSource.SomeEvent, New EventHandler(Of SomeEventArgs)(AddressOf OnSomeEvent)
        End Sub
    
        ''' <summary>
        ''' Stop listening to the given source for the event.
        ''' </summary>
        Protected Overrides Sub StopListening(source As Object)
            Dim typedSource As SomeEventSource = CType(source, SomeEventSource)
            AddHandler typedSource.SomeEvent, New EventHandler(Of SomeEventArgs)(AddressOf OnSomeEvent)
        End Sub
    
        ''' <summary>
        ''' Event handler for the SomeEvent event.
        ''' </summary>
        Private Sub OnSomeEvent(sender As Object, e As SomeEventArgs)
            DeliverEvent(sender, e)
        End Sub
    End Class
    
  2. , SomeEvent, SomeEventSourceve SomeEventArgs öğesini olay adınızla eşleşecek şekilde yeniden adlandırınSomeEventWeakEventManager.

  3. Zayıf olay yöneticisi sınıfının erişim değiştiricilerini, yönettiği olayın erişilebilirliğiyle eşleşecek şekilde ayarlayın.

  4. Normal olay bağlaması yerine yeni zayıf olay yöneticisini kullanın.

    Örneğin, kodunuz bir olaya abone olmak için aşağıdaki deseni kullanıyorsa:

    source.SomeEvent += new EventHandler<SomeEventArgs>(Source_SomeEvent);
    
    AddHandler source.SomeEvent, New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent)
    

    Bunu aşağıdaki desenle değiştirin:

    SomeEventWeakEventManager.AddHandler(source, Source_SomeEvent);
    
    SomeEventWeakEventManager.AddHandler(
        source, New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent))
    

    Benzer şekilde, kodunuz bir olayın aboneliğini kaldırmak için aşağıdaki deseni kullanıyorsa:

    source.SomeEvent -= new EventHandler<SomeEventArgs>(Source_SomeEvent);
    
    RemoveHandler source.SomeEvent, New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent)
    

    Bunu aşağıdaki desenle değiştirin:

    SomeEventWeakEventManager.RemoveHandler(source, Source_SomeEvent);
    
    SomeEventWeakEventManager.RemoveHandler(
        source, New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent))
    

Ayrıca bkz.