2015-11-25 18 views
6

Nel mio codice ho bisogno di annullare la registrazione e registrare un gestore di eventi, questo funziona perfetto:Come annullare la registrazione e registrare un gestore eventi attendibile?

_favoritsImageView.Click -= _favoritsImageView_Click(this, new CustomeClickEventArgs(item)); 
_favoritsImageView.Click += _favoritsImageView_Click(this, new CustomeClickEventArgs(item)); 
void _favoritsImageView_Click(object sender, CustomeClickEventArgs e) 
{ 
     // handles the event 
} 

Ma per un awaitable gestore eventi devo usare questa sintassi:

_favoritsImageView.Click -= async (s, e) => 
{ await _favoritsImageView_ClickAsync(s, new CustomeClickEventArgs(item)); }; 

_favoritsImageView.Click += async (s, e) => 
{ await _favoritsImageView_ClickAsync(s, new CustomeClickEventArgs(item)); }; 

async Task _favoritsImageView_ClickAsync(object sender, CustomeClickEventArgs e) 
{ 
     // does async task 
} 

Questo fa non funziona. Perché i metodi anonimi non hanno lo stesso riferimento. Quindi la prima riga non annulla la registrazione del gestore già registrato. E alla fine la seconda riga aggiunge un gestore di eventi extra al clic.

Quale sintassi è necessario utilizzare per aggiungere e rimuovere un gestore di eventi asincroni?

Grazie per qualsiasi suggerimento.

+0

Sono confuso, la sintassi che si dice "funziona perfettamente" non può funzionare. A meno che '_favoritsImageView_Click' fosse un metodo che restituisce un delegato, il che sarebbe davvero strano. – svick

+0

@svick Ho aggiunto '_favoritsImageView_Click' al codice di esempio. Cosa c'è che non va?? –

+0

Ciò che è sbagliato è che la riga '_favoritsImageView.Click + = _favoritsImageView_Click (questo, nuovo CustomeClickEventArgs (elemento));' non verrà compilato. – svick

risposta

5

Which syntax do I need to use to add and remove an async event handler?

La stessa sintassi che è necessario utilizzare con i gestori di eventi regolari. È necessario salvare il delegato da qualche parte in modo da poter in seguito de-registrarlo:

private EventHandler eventHandler = 
          new EventHandler(async (s, e) => await FooAsync(s, e)); 

public async void SomeOtherEventHandler() 
{ 
    var m = new M(); 
    m.X += eventHandler; 
    m.OnFoo(); 
    m.X -= eventHandler; 
    m.OnFoo(); 
} 

public async Task FooAsync(object sender, EventArgs e) 
{ 
    await Task.Delay(1000); 
    Debug.WriteLine("Yay event handler"); 
} 

public class M 
{ 
    public event EventHandler X; 
    public void OnX() 
    { 
     // If you're using C#-6, then X?.Invoke(null, EventArgs.Empty); 

     var localX = X; 
     if (localX != null) 
      localX(null, EventArgs.Empty); 
    } 
} 

Edit:

@svick suggerisce forse un'altra soluzione, per fare semplicemente il metodo async void e registrarlo direttamente, il che è decisamente più breve:

public async void SomeOtherEventHandler() 
{ 
    var m = new M(); 
    m.X += FooAsync; 
    m.OnFoo(); 
    m.X -= FooAsync; 
    m.OnFoo(); 
} 

public async void FooAsync(object sender, EventArgs e) 
{ 
    await Task.Delay(1000); 
    Debug.WriteLine("Yay event handler"); 
} 
+0

È necessario salvare il delegato solo se il gestore eventi è un lambda. Penso che, in generale, una soluzione migliore sia scrivere il gestore di eventi come metodo (metodo 'async void' in questo caso), che renderebbe il codice più semplice. – svick

+0

@svick Questa è una buona idea. Aggiunta un'altra versione con la registrazione diretta. –

+0

Penso che potrebbe essere bello sapere qual è la differenza tra un ritorno Task e un ritorno vuoto in questo caso, e perché la risposta @svick è buona :) https://stackoverflow.com/a/8043882/6400617 –

1

Il codice sincrono simile a questa:

private void FavoritsImageView_Click(object sender, CustomeClickEventArgs args) 
{ 
    // your synchronous code here 
} 

… 

_favoritsImageView.Click += FavoritsImageView_Click; 
_favoritsImageView.Click -= FavoritsImageView_Click; 

La versione async sembra quasi lo stesso, è necessario solo aggiungere async al metodo:

private async void FavoritsImageView_Click(object sender, CustomeClickEventArgs args) 
{ 
    // your asynchronous code here 
} 

… 

_favoritsImageView.Click += FavoritsImageView_Click; 
_favoritsImageView.Click -= FavoritsImageView_Click; 

Si noti che un gestore di eventi è praticamente l'unico posto dove si dovrebbe usare async void. Nella maggior parte delle altre situazioni, un metodo sincrono void deve essere convertito nel metodo async Task.

+0

I l'ho già provato Errore del compilatore: Non è possibile convertire implicitamente il tipo 'void' in 'System.EventHandler' –

+0

@ a.toraby. Ricevo questo errore quando uso la sintassi '+ = FavoritsImageView_Click (questo, nuovo CustomeClickEventArgs (elemento));'. Ma non è quello che ho suggerito. – svick

+0

@downvoter Ti va di spiegare cosa pensi che sia sbagliato con la mia risposta? – svick