2010-07-15 3 views
8

Puoi vedere gli svantaggi di questo one-liner diverso dal fatto che più usi di esso violerebbero il principio DRY? Sembra semplice ma il fatto di non averlo visto proporre da altri mi fa chiedere se c'è un lato negativo.One Liner: WeakReference-to-a-Lambda Event Handler

Questo bit di codice crea un WeakReference in un metodo e quindi registra un gestore di eventi che richiama la destinazione del riferimento.

SomeEvent += (sender, e) => ((Action)(new WeakReference((Action)ProcessEvent)).Target)(); 

Grazie,
Ben

+0

Hai visto la mia domanda/risposta [qui] (http://stackoverflow.com/questions/1747235/weak-event-handler-model-for-use-with-lambdas)? Non è un one-liner, ma io * penso * funzioni ... – Benjol

risposta

11

Non penso che lo schema faccia quello che ti aspetti. Stai cercando di impedire all'evento di mantenere un riferimento all'oggetto corrente in modo da prevenire perdite di memoria? L'espressione lambda acquisirà il valore di this per valutare ProcessEvent (supponendo che lo ProcessEvent sia un metodo di istanza), quindi si verificherà ancora la perdita. Questo codice è lo stesso di SomeEvent += (sender, e) => ProcessEvent();.

Si può tentare fare qualcosa di più come questo (che non è anche quello che si vuole):

var reference = new WeakReference((Action)ProcessEvent); 
SomeEvent += (sender, e) => ((Action)reference.Target)(); 

Ora l'espressione lambda catturerà l'WeakReference, così non avrete un riferimento forte a this. Sfortunatamente, nient'altro fa riferimento al delegato creato da ProcessEvent, quindi verrà rimosso sul prossimo GC anche se this è ancora attivo. (Questo inoltre non controlla se Target è nullo).

Si potrebbe provare qualcosa di simile:

public EventHandler MakeWeakHandler(Action action, Action<EventHandler> remove) 
{ 
    var reference = new WeakReference(action.Target); 
    var method = action.Method; 
    EventHandler handler = null; 
    handler = delegate(object sender, EventArgs e) 
    { 
     var target = reference.Target; 
     if (target != null) 
     { 
      method.Invoke(target, null); 
     } 
     else 
     { 
      remove(handler); 
     } 
    }; 
    return handler; 
} 

e quindi utilizzarlo in questo modo:

SomeEvent += MakeWeakHandler(ProcessEvent, h => SomeEvent -= h); 

che manterrà debole di riferimento al ricevitore di processEvent, e rimuoverà automaticamente l'evento gestore dall'evento dopo che è stato raccolto, il che dovrebbe impedire perdite di memoria finché l'evento viene generato regolarmente.

+0

Questo non funziona per nessun evento. È necessario specializzarlo per l'evento * esatto * richiesto, ad esempio, sostituendo EventArgs con PropertyChangedEventArgs e EventHandler con PropertyChangedEventHandler. Ho provato a creare una versione generica in cui è possibile passare i tipi ma non sembra funzionare. –

0

La sua non è molto leggibile. E se si deve eseguire il debug di questo passaggio, nessuna di queste azioni potrebbe fallire, ma quella singola riga fallirebbe. Inoltre, otterresti solo quella singola linea referenziata in una traccia dello stack.

In genere si vuole evitare di fare troppe cose in una singola riga per la manutenibilità.