2010-12-09 4 views
0

Nous utilisons des événements comme celui-ci lamda:En C#, comment un événement asynchrone terminé doit-il accéder aux propriétés de la classe appelante?

bookMarkClient.wmdeleteCompleted += (s, ea) => 
     { 
      if (ea.Result = "Success") 
      { 
       foreach (BookMark bookMark in BookMarks) 
        { 
         if (bookMarkId == bookMark.bm_id) 
         { 
          BookMarks.Remove(bookMark); 
          OnNotifyPropertyChanged("BookMarks"); 
          break; 
         } 
        }     
       } 
     }; 

     bookMarkClient.wmdeleteBookMarkAsync(bookMarkId); 

Toutefois, selon de nombreuses réponses sur stackoverflow ceux-ci ne peuvent pas être supprimés, de sorte que si je l'appelle à nouveau le code de mon événement est déclenché deux fois. Donc, je veux supprimer le code lambda et faire ceci:

//in the class constructor 
bookMarkClient.wmdeleteBookMarkCompleted += new EventHandler<wmdeleteBookMarkCompletedEventArgs>(bookMarkClient_wmdeleteBookMarkCompleted); 

//Proc on the same class 
void bookMarkClient_wmdeleteBookMarkCompleted(object sender, wmdeleteBookMarkCompletedEventArgs e) 
     { 
      if (ea.Result = "Success") 
      { 
       foreach (BookMark bookMark in BookMarks) 
       { 
        if (bookMarkId == bookMark.bm_id) 
        { 
         BookMarks.Remove(bookMark); 
         OnNotifyPropertyChanged("BookMarks"); 
         break; 
        } 
       } 
      } 

       } 

//on button click 
bookMarkClient.wmdeleteBookMarkAsync(bookMarkId); 

CEPENDANT: la bookMarkId variable n'est plus disponible. Je comprends que c'est parce que l'événement lambda copie la variable dans la classe construite et la garde en vie pendant la durée de l'événement. Alors, comment faire la même chose?

J'ai essayé de définir une variable privée mais le test montre que cela peut être changé avant la réception du rappel! Cela s'appliquerait également à la collection appelée Bookmarks.

Remarque, Il s'agit d'un client Silverlight et je préfère ne pas renvoyer l'ID/collection dans les arguments d'événement car cela signifierait retravailler beaucoup de code wcf.

Merci beaucoup, Matt

ps premier poste à pile afin de prendre facilement sur moi ...

Répondre

2

magasin le gestionnaire d'événements dans une variable locale; alors vous pouvez ajouter et supprimer le même gestionnaire d'événements:

EventHandler<wmdeleteBookMarkCompletedEventArgs> handler = (s, ea) => { .... } 
bookMarkClient.wmdeleteBookMarkCompleted += handler; 
// ... 
bookMarkClient.wmdeleteBookMarkCompleted -= handler; 

Alternativement, une expression lambda est juste du sucre syntaxique pour un compilateur généré classe imbriquée. Si vous ne pouvez pas ajouter l'ID de signet aux EventArgs, simuler un lambda en créant votre propre classe:

BookmarkDeletedListener listener = new BookmarkDeletedListener(this, bookMarkId); 
bookMarkClient.wmdeleteBookMarkCompleted += listener.DeleteBookmarkCompleted; 
bookMarkClient.wmdeleteBookMarkCompleted -= listener.DeleteBookmarkCompleted; 

// ... 

class BookmarkDeletedListener 
{ 
    public BookmarkDeletedListener(ParentClass parent, string bookmarkId) 
    { 
     _parent = parent; 
     _bookmarkId = bookmarkId; 
    } 

    public DeleteBookmarkCompleted(object sender, wmdeleteBookMarkCompletedEventArgs e) 
    { 
     if (ea.Result = "Success") 
     { 
      foreach (BookMark bookMark in BookMarks) 
      { 
       if (_bookmarkId == bookMark.bm_id) 
       { 
        _parent.BookMarks.Remove(bookMark); 
        _parent.OnNotifyPropertyChanged("BookMarks"); 
        break; 
       } 
      } 
     } 
    } 

    readonly ParentClass _parent; 
    readonly string _bookmarkId; 
} 
+0

Merci, comme dans ma réponse à l'autre réponse, j'ai essayé de supprimer le lambda en utilisant une variable locale - avec des résultats mitigés. Votre solution semble faisable mais je ne suis pas sûr d'où déclarer l'écouteur, puis de +/- le gestionnaire. Si je l'ajoute, appelez la méthode async puis supprimez-la sur la ligne suivante, le callback n'est jamais reçu! Enfin, il me force à faire des marque-pages et onNotifyPropertyChanged public (pas idéal). Matt – MattPil29

0

Vous pouvez supprimer un lambda, AFAIK, aussi longtemps que vous tenez sur elle. Il peut même se retirer quand il est tiré:

Action<object, wmdeleteBookMarkCompletedEventArgs> handler; 
handler = 
    (s, ea) => 
     { 
      bookMarkClient.wmdeleteCompleted -= handler; 
      if (ea.Result == "Success") 
      ... 
     }; 

bookMarkClient.wmdeleteCompleted += handler; 
+0

Merci pour la réponse rapide. Cela donne une erreur "Utilisation de la variable locale non affectée" gestionnaire "". Peu importe si j'ai déjà du code comme ça, mais j'aimerais passer à l'utilisation d'événements qui sont plus clairement attachés au client lors de sa création. Donc, les questions restent en suspens, comment puis-je avoir accès aux propriétés de la classe [dans l'état dans lequel elles se trouvaient lorsque la méthode async a été appelée]? – MattPil29

+0

Suivi: si le gestionnaire est défini comme une variable privée sur la classe, l'exemple ci-dessus [semble fonctionner]. Cependant, si le gestionnaire est défini comme une variable locale (à l'intérieur du proc appelant), le compilateur se plaint. Je ne sais toujours pas vraiment la réponse à ma question! – MattPil29

+1

Le code manque un petit détail. La première ligne a besoin de "= null" à ajouter entre "handler" et ";". Ceci est le résultat des règles d '"assignation définitive" de C#. (Le compilateur est sans doute un peu paranoïaque ici, mais il est assez facile de contourner cela.) –

Questions connexes