2010-11-13 9 views
27

He 3 preguntas sobre los sucesos:¿Debo darme de baja de los eventos?

  1. Siempre debo eventos para darse de baja que se suscribieron?
  2. ¿Qué sucede si NO?
  3. En los ejemplos a continuación, ¿cómo cancelaría la suscripción a los eventos suscritos?

tengo, por ejemplo, este código:

Ctor: Objetivo: Se actualizaciones de base de datos propiedad

this.PropertyChanged += (o, e) => 
{ 
    switch (e.PropertyName) 
    { 
     case "FirstName": break; 
     case "LastName": break; 
    } 
}; 

y esto: Objetivo: Se GUI vinculante envolver el modelo en ViewModels

ObservableCollection<Period> periods = _lpRepo.GetDailyLessonPlanner(data.DailyDate); 
PeriodListViewModel = new ObservableCollection<PeriodViewModel>(); 

foreach (Period period in periods) 
{ 
    PeriodViewModel periodViewModel = new PeriodViewModel(period,_lpRepo); 
    foreach (DocumentListViewModel documentListViewModel in periodViewModel.DocumentViewModelList) 
    { 
     documentListViewModel.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument); 
     documentListViewModel.AddDocumentDelegate += new Action(OnAddDocument); 
     documentListViewModel.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument); 
    } 
    PeriodListViewModel.Add(periodViewModel); 
} 
+0

http: // stackoverflow.com/questions/1061727/is-it-bad-to-not-unregister-event-handlers – SwDevMan81

Respuesta

23

1) Depende. Por lo general, es una buena idea, pero hay casos típicos en los que no es necesario. Básicamente, si está seguro de que el objeto suscrito va a sobrevivir al origen del evento, debe darse de baja, de lo contrario, esto crearía una referencia innecesaria.

Sin embargo, si el objeto está suscribiendo a sus propios eventos, como en el siguiente:

<Window Loaded="self_Loaded" ...>...</Window> 

--¡entonces usted no tenga que hacerlo.

2) La suscripción a un evento hace referencia adicional al objeto suscrito. Por lo tanto, si no se da de baja, su objeto se mantendrá vivo con esta referencia, lo que lo convierte en una pérdida de memoria. Al desuscribirse, está eliminando esa referencia. Tenga en cuenta que en el caso de la auto-suscripción, el problema no surge.

3) Se puede hacer así:

this.PropertyChanged += PropertyChangedHandler; 
... 
this.PropertyChanged -= PropertyChangedHandler; 

donde

void PropertyChangedHandler(object o, PropertyChangedEventArgs e) 
{ 
    switch (e.PropertyName) 
    { 
     case "FirstName": break; 
     case "LastName": break; 
    } 
} 
+0

cuando uso el código inferior no puedo compilar? Yo puse un ";" detrás del último "}" pero no está compilando? ¿Y dónde debo anular la suscripción con su código sugerido? – Elisabeth

+0

@Lisa: (1) ¿cuál es el mensaje de error que recibe? (2) debe darse de baja cuando ya no necesite los eventos. A menudo, esto es al final de la vida de su objeto. Si pudieras saber más sobre el objeto que se va a suscribir para los eventos, sería más fácil entender cuándo cancelar la suscripción. – Vlad

+0

Verifique mi código anterior Suscribo 3 eventos en métodos de XXX. Un PeriodViewModel no se puede eliminar un DocumentListViewModel tampoco. – Elisabeth

4

Puede echar un vistazo a this article en MSDN. Cita:

para evitar que su controlador de eventos de que se invoca cuando el evento es planteado, simplemente darse de baja del evento . Para evitar fugas de recursos , es importante anular la suscripción a de eventos antes de deshacerse de un objeto de abonado . Hasta que darse de baja de un evento, el delegado de multidifusión que subyace en el caso en el objeto de la publicación tiene una referencia al delegado que encapsula caso manejador del suscriptor. Siempre que el objeto de publicación contenga esa referencia, su objeto de suscriptor no será basura recopilada.

1

1.) ¿Debo siempre desubscribe eventos que fueron suscritas?
Normalmente, sí.La única excepción es cuando el objeto al que se suscribió ya no se referencia y pronto se recolectará basura.

2.) ¿Qué sucede si NO?
El objeto al que se suscribió mantendrá una referencia al delegado, que a su vez guarda una referencia a su puntero this, y así obtendrá una pérdida de memoria.
O si el manejador era un lamda se mantendrá en cualquier variable local que se vincule, que tampoco se recogerá.

+1

Quiero señalar que si no cancela la suscripción al evento, se puede llamar a su controlador después de que haya terminado de usar el objeto; si el objeto es desechable luego de que el objeto haya sido eliminado. Esto puede tener un impacto en el código del controlador si utiliza recursos no administrados que han sido liberados por el usuario. –

39

Bien, tomemos la última pregunta primero. Usted no puede anular la suscripción de un evento al que se haya suscrito directamente con una expresión lambda. Usted bien necesita mantener una variable con el delegado en (por lo que aún puede usar una expresión lambda) o necesita usar una conversión de grupo de métodos en su lugar.

Ahora, si realmente necesita darse de baja, depende de la relación entre el productor del evento y el consumidor del evento. Si el productor del evento debe vivir más tiempo que el consumidor del evento, usted debe anular la suscripción a, porque de lo contrario el productor tendrá una referencia al consumidor, manteniéndolo con vida por más tiempo de lo que debería. El controlador de eventos también seguirá recibiendo llamadas mientras el productor lo produzca.

Ahora, en muchos casos, eso no es un problema, por ejemplo, en una forma el botón que provoca el evento Click es probable que viva tanto tiempo como la forma en que se creó, donde el controlador está suscrito normalmente. así que no hay necesidad de darse de baja. Esto es muy típico para una GUI.

Del mismo modo, si se crea un WebClient con el único fin de una única solicitud asincrónica, se suscribe al evento relevante y empezar la solicitud asíncrona, entonces el WebClient sí serán elegibles para la recolección de basura cuando la petición ha terminado (que asumir Don mantener una referencia en otro lugar).

Básicamente, siempre debe considerar la relación entre el productor y el consumidor. Si el productor va a vivir más de lo que desea que el consumidor, o va a seguir aumentando el evento después de que ya no esté interesado en él, entonces debe darse de baja.

+0

event producer = documentListViewModel? consumidor del evento = un método OnXXX desde arriba? ¿Quiere decir que si voy a eliminar un documentListViewModel todavía hay una referencia a un método OnXXX? ¿Dónde debería cancelar la suscripción a eventos en un ViewModel? – Elisabeth

2

No tiene que darse de baja de un evento cuando la instancia que se suscribe tiene el mismo alcance que la instancia a la que se está suscrito.

Digamos que usted es un formulario y se suscribe a un control, estos dos forman un grupo. Sin embargo, si tiene una clase central que gestiona formularios y se ha suscrito al evento Closed de ese formulario, estos no forman un grupo y debe darse de baja una vez que se cierra el formulario.

La suscripción a un evento hace que la instancia suscrita cree una referencia a la instancia a la que se está suscrito. Esto evita la recolección de basura. Por lo tanto, cuando tiene una clase central que administra instancias de formulario, esto mantendrá todas las formas en la memoria.

WPF es una excepción porque tiene un modelo de evento débil donde los eventos están suscritos utilizando referencias débiles y no guardará el formulario en la memoria. Sin embargo, sigue siendo una buena práctica darse de baja cuando no formes parte del formulario.

Cuestiones relacionadas