Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En las aplicaciones, es posible que los controladores asociados a los orígenes de eventos no se destruirán en coordinación con el objeto de escucha que adjuntó el controlador al origen. Esta situación puede provocar pérdidas de memoria. Windows Presentation Foundation (WPF) presenta un patrón de diseño que se puede usar para solucionar este problema. El patrón de diseño proporciona una clase de administrador dedicada para eventos particulares e implementa una interfaz en los escuchadores para ese evento. Este patrón de diseño se conoce como el patrón de eventos débiles.
Prerrequisitos
El artículo supone un conocimiento básico de los eventos enrutados y que hayas leído Información general sobre eventos enrutados. Para seguir los ejemplos de este artículo, le ayuda si está familiarizado con el lenguaje de marcado extensible de aplicaciones (XAML) y sabe cómo escribir aplicaciones de Windows Presentation Foundation (WPF).
¿Por qué implementar el patrón de eventos débiles?
La escucha de eventos puede provocar pérdidas de memoria. La técnica habitual para escuchar un evento es usar la sintaxis específica del lenguaje para adjuntar un controlador a un evento en un origen. Por ejemplo, la instrucción source.SomeEvent += new SomeEventHandler(MyEventHandler) C# o la instrucción AddHandler source.SomeEvent, AddressOf MyEventHandlerVB . Sin embargo, esta técnica crea una referencia segura desde el origen del evento al agente de escucha de eventos. A menos que se desregistre explícitamente el controlador de eventos, la vida útil del objeto de la escucha se verá afectada por la vida útil del objeto del origen. En determinadas circunstancias, es posible que desee que la duración del objeto del oyente sea controlada por otros factores, como si actualmente forma parte del árbol visual de la aplicación. Cada vez que la duración del objeto del origen se extiende más allá de la duración útil del objeto del agente de escucha, el agente de escucha se mantiene activo durante más tiempo de lo necesario. En este caso, la memoria sin asignar equivale a una pérdida de memoria.
El patrón de eventos débiles está diseñado para resolver el problema de pérdida de memoria. El patrón de eventos débil se puede usar cuando un agente de escucha necesita registrarse para un evento, pero el agente de escucha no sabe explícitamente cuándo anular el registro. El patrón de eventos débiles también se puede usar cuando la vida útil del objeto fuente supera la vida útil del objeto escucha. En este caso, usted determina que es útil . El patrón de evento débil permite que el agente de escucha se registre y reciba el evento sin afectar a las características de duración del objeto del agente de escucha de ninguna manera. En efecto, la referencia implícita del origen no determina si el escucha de eventos es apto para la recolección de basura. La referencia es una referencia débil, por lo que la nomenclatura del patrón de eventos débil y las API relacionadas. El oyente se puede recoger la basura o destruirse de otra manera, y la fuente puede continuar sin conservar las referencias de controlador no recolectables a un objeto ahora destruido.
¿Quién debe implementar el patrón de eventos débiles?
El patrón de eventos débiles es principalmente relevante para los autores de controles. Como autor de un control, es en gran medida responsable del comportamiento y la contención del control y el impacto que tiene en las aplicaciones en las que se inserta. Esto incluye el comportamiento de vida útil del objeto del control, en particular el control del problema de pérdida de memoria descrito.
Ciertos escenarios se prestan inherentemente a la aplicación del patrón de eventos débiles. Uno de estos escenarios es el enlace de datos. En la vinculación de datos, es habitual que el objeto de origen sea independiente del objeto oyente, que es un destino del enlace. Muchos aspectos del enlace de datos de WPF ya tienen el patrón de eventos débil aplicado en cómo se implementan los eventos.
Cómo implementar el patrón de eventos débiles
Hay cuatro maneras de implementar el patrón de eventos débiles y cada enfoque usa un administrador de eventos diferente. Seleccione el administrador de eventos que mejor se adapte a su escenario.
Administrador de eventos débil existente:
Use una clase de administrador de eventos débil existente cuando el evento al que desea suscribirse tenga un elemento correspondiente WeakEventManager. Para obtener una lista de administradores de eventos débiles que se incluyen con WPF, consulte la jerarquía de herencia en la clase
WeakEventManager. Dado que los administradores de eventos débiles incluidos son limitados, probablemente tendrá que elegir uno de los otros enfoques.Administrador de eventos débil genérico:
Use un genérico WeakEventManager<TEventSource,TEventArgs> cuando un existente WeakEventManager no esté disponible y busque la manera más fácil de implementar eventos débiles. Sin embargo, el genérico
WeakEventManager<TEventSource,TEventArgs>es menos eficaz que el administrador de eventos débil existente o personalizado porque usa la reflexión para detectar el evento a partir de su nombre. Además, el código necesario para registrar el evento mediante el genéricoWeakEventManager<TEventSource,TEventArgs>es más extenso que usar un objeto existente o personalizadoWeakEventManager.Administrador de eventos débil personalizado:
Crear un WeakEventManager personalizado cuando un
WeakEventManagerexistente no está disponible y la eficiencia es crucial. Aunque es más eficaz que un genéricoWeakEventManager, un personalizadoWeakEventManagerrequiere que escriba más código inicial.Administrador de eventos débil de terceros:
Use un administrador de eventos débil de terceros cuando necesite funcionalidad que no proporcionen los otros enfoques. NuGet tiene algunos administradores de eventos débiles. Muchos marcos de WPF también admiten el patrón.
En las secciones siguientes se describe cómo implementar el patrón de eventos débil mediante el uso de los distintos tipos de administrador de eventos. En el caso de los ejemplos genéricos y personalizados del administrador de eventos débiles, el evento al que suscribirse tiene las siguientes características.
- El nombre del evento es
SomeEvent. - La clase genera el
SomeEventSourceevento . - El controlador de eventos tiene el tipo
EventHandler<SomeEventArgs>. - El evento pasa un parámetro de tipo
SomeEventArgsa los controladores de eventos.
Uso de una clase de administrador de eventos débil existente
Busque un administrador de eventos débil existente. Para obtener una lista de administradores de eventos débiles incluidos con WPF, consulte la jerarquía de herencia de la WeakEventManager clase .
Use el nuevo administrador de eventos débiles en lugar del enlace de eventos normal.
Por ejemplo, si el código usa el siguiente patrón para suscribirse a un evento:
source.LostFocus += new RoutedEventHandler(Source_LostFocus);AddHandler source.LostFocus, New RoutedEventHandler(AddressOf Source_LostFocus)Cámbielo por el siguiente patrón:
LostFocusEventManager.AddHandler(source, Source_LostFocus);LostFocusEventManager.AddHandler( source, New EventHandler(Of RoutedEventArgs)(AddressOf Source_LostFocus))Del mismo modo, si el código usa el siguiente patrón para cancelar la suscripción de un evento:
source.LostFocus -= new RoutedEventHandler(Source_LostFocus);RemoveHandler source.LostFocus, New RoutedEventHandler(AddressOf Source_LostFocus)Cámbielo por el siguiente patrón:
LostFocusEventManager.RemoveHandler(source, Source_LostFocus);LostFocusEventManager.RemoveHandler( source, New EventHandler(Of RoutedEventArgs)(AddressOf Source_LostFocus))
Utilice la clase de administrador de eventos débil genérica
Use la clase genérica WeakEventManager<TEventSource,TEventArgs> en lugar del enlace de eventos normal.
Cuando usas WeakEventManager<TEventSource,TEventArgs> para registrar escuchadores de eventos, proporcionas la fuente del evento y el tipo EventArgs como parámetros de tipo a la clase. Llame a AddHandler como se muestra en el código siguiente:
WeakEventManager<SomeEventSource, SomeEventArgs>.AddHandler(source, "SomeEvent", Source_SomeEvent);
WeakEventManager(Of SomeEventSource, SomeEventArgs).AddHandler(
source, "SomeEvent", New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent))
Creación de una clase de administrador de eventos débil personalizada
Copie la siguiente plantilla de clase en el proyecto. La siguiente clase hereda de la WeakEventManager clase :
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 ClassCambie el nombre
SomeEventWeakEventManagerde ,SomeEvent,SomeEventSourceySomeEventArgspara que coincida con el nombre del evento.Establezca los modificadores de acceso de la clase de administrador de eventos débiles para que coincidan con la accesibilidad del evento que administra.
Use el nuevo administrador de eventos débiles en lugar del enlace de eventos normal.
Por ejemplo, si el código usa el siguiente patrón para suscribirse a un evento:
source.SomeEvent += new EventHandler<SomeEventArgs>(Source_SomeEvent);AddHandler source.SomeEvent, New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent)Cámbielo por el siguiente patrón:
SomeEventWeakEventManager.AddHandler(source, Source_SomeEvent);SomeEventWeakEventManager.AddHandler( source, New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent))Del mismo modo, si el código usa el siguiente patrón para cancelar la suscripción a un evento:
source.SomeEvent -= new EventHandler<SomeEventArgs>(Source_SomeEvent);RemoveHandler source.SomeEvent, New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent)Cámbielo por el siguiente patrón:
SomeEventWeakEventManager.RemoveHandler(source, Source_SomeEvent);SomeEventWeakEventManager.RemoveHandler( source, New EventHandler(Of SomeEventArgs)(AddressOf Source_SomeEvent))
Consulte también
.NET Desktop feedback