Non esiste un metodo semplice per raggiungere questo obiettivo.
approccio preferito:
non vedo il motivo per cui non è possibile salvare il delegato. Non è necessario salvare l'istanza come un campo. Può essere una variabile locale che viene catturata dal vostro gestore di eventi anonimo:
EventHandler<TypeOfEventArgs> handler = null;
handler = (s, e) =>
{
// Do whatever you need to do here
// Remove event:
foo.Event -= handler;
}
foo.Event += handler;
non riesco a pensare ad un unico scenario in cui non è possibile utilizzare questo.
approccio alternativo senza salvare il delegato:
Tuttavia, se si dispone di un tale scenario, è ottenere abbastanza complicato.
È necessario trovare il delegato che è stato aggiunto come gestore all'evento. Perché non l'hai salvato, è piuttosto difficile ottenerlo. Non c'è this
per ottenere un delegato del metodo attualmente in esecuzione.
Non è possibile utilizzare GetInvocationList()
sull'evento sia, perché l'accesso a un evento al di fuori della classe è definita in è limitato a aggiungere e rimuovere i gestori, vale a dire +=
e -=
.
Impossibile creare un nuovo delegato. Mentre è possibile accedere all'oggetto MethodInfo
che definisce il metodo anonimo, non è possibile accedere all'istanza della classe in cui è dichiarato il metodo. Questa classe viene generata automaticamente dal compilatore e chiamando lo this
all'interno del metodo anonimo restituirà il istanza della classe in cui è definito il metodo normale.
L'unico modo in cui ho trovato che funziona è trovare il campo, se presente, che l'evento utilizza e chiamare GetInvocationList()
su di esso. Il codice seguente illustra questo con una classe fittizia:
void Main()
{
var foo = new Foo();
foo.Bar += (s, e) => {
Console.WriteLine("Executed");
var self = new StackFrame().GetMethod();
var eventField = foo.GetType()
.GetField("Bar", BindingFlags.NonPublic |
BindingFlags.Instance);
if(eventField == null)
return;
var eventValue = eventField.GetValue(foo) as EventHandler;
if(eventValue == null)
return;
var eventHandler = eventValue.GetInvocationList()
.OfType<EventHandler>()
.FirstOrDefault(x => x.Method == self)
as EventHandler;
if(eventHandler != null)
foo.Bar -= eventHandler;
};
foo.RaiseBar();
foo.RaiseBar();
}
public class Foo
{
public event EventHandler Bar;
public void RaiseBar()
{
var handler = Bar;
if(handler != null)
handler(this, EventArgs.Empty);
}
}
Si prega di notare che la stringa "Bar"
che viene passata al GetField
deve essere il nome esatto del campo che viene utilizzato dall'evento. Ciò comporta due problemi:
- Il campo può essere denominato diversamente, ad es. quando si utilizza un'implementazione di un evento esplicito. Devi trovare manualmente il nome del campo.
- Potrebbe non esserci alcun campo. Ciò accade se l'evento utilizza un'implementazione di un evento esplicito e si limita a delegare a un altro evento o a memorizzare i delegati in qualche altro modo.
Conclusione:
L'approccio alternativo si basa su dettagli di implementazione, in modo da non utilizzare, se si può evitare.
C'è un paio di modelli là fuori che ti permettono di sottoscrivere gli eventi in un * Loosley accoppiato * modo. Dai un'occhiata a [questo] (http://martinfowler.com/eaaDev/EventAggregator.html). Descrive come iscriversi/annullare l'iscrizione a un aggregatore di eventi (in questo caso si tratta di schermate). L'aggregatore effettivo crea i riferimenti tra gli oggetti. Ci sono molte implementazioni differenti là fuori. Quello che preferisco è implementato in [Caliburn.Micro] (http://www.mindscapehq.com/blog/index.php/2012/02/01/caliburn-micro-part-4-the-event-aggregator/) . –