Si vous pouvez utiliser le Extensions réactives pour .NET vous pouvez simplifier cela.
Vous pouvez faire un Observable à partir d'un événement et n'écouter que le premier élément en utilisant .Take(1)
pour réaliser votre petit bout de code. Tout le processus se résume ainsi à quelques lignes de code.
Edit : Afin de démontrer, j'ai fait un programme d'exemple complet (je vais le coller ci-dessous).
J'ai déplacé la création et l'abonnement de l'observable dans une méthode ( HandleOneShot
). Cela vous permet de faire ce que vous essayez de faire avec un seul appel de méthode. Pour la démonstration, j'ai créé une classe avec deux propriétés qui implémente INotifyPropertyChanged, et j'écoute l'appel à la méthode premièrement l'événement de changement de propriété, en écrivant dans la console quand il se produit.
Cela prend votre code, et le change en :
HandleOneShot<SomeEventArgs>(variableOfSomeType, "SomeEvent", e => {
// Small snippet of code here
});
Notez que toutes les opérations d'abonnement et de désabonnement se déroulent automatiquement pour vous en coulisses. Il n'est pas nécessaire de gérer l'abonnement manuellement - il suffit de s'abonner à l'Observable, et Rx s'en occupe pour vous.
Lorsqu'il est exécuté, ce code s'imprime :
Setup...
Setting first property...
**** Prop2 Changed! /new val
Setting second property...
Setting first property again.
Press ENTER to continue...
Vous n'avez qu'une seule chance de déclencher votre événement.
namespace ConsoleApplication1
{
using System;
using System.ComponentModel;
using System.Linq;
class Test : INotifyPropertyChanged
{
private string prop2;
private string prop;
public string Prop
{
get {
return prop;
}
set
{
if (prop != value)
{
prop = value;
if (PropertyChanged!=null)
PropertyChanged(this, new PropertyChangedEventArgs("Prop"));
}
}
}
public string Prop2
{
get
{
return prop2;
}
set
{
if (prop2 != value)
{
prop2 = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Prop2"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
class Program
{
static void HandleOneShot<TEventArgs>(object target, string eventName, Action<TEventArgs> action) where TEventArgs : EventArgs
{
var obsEvent = Observable.FromEvent<TEventArgs>(target, eventName).Take(1);
obsEvent.Subscribe(a => action(a.EventArgs));
}
static void Main(string[] args)
{
Test test = new Test();
Console.WriteLine("Setup...");
HandleOneShot<PropertyChangedEventArgs>(
test,
"PropertyChanged",
e =>
{
Console.WriteLine(" **** {0} Changed! {1}/{2}!", e.PropertyName, test.Prop, test.Prop2);
});
Console.WriteLine("Setting first property...");
test.Prop2 = "new value";
Console.WriteLine("Setting second property...");
test.Prop = "second value";
Console.WriteLine("Setting first property again...");
test.Prop2 = "other value";
Console.WriteLine("Press ENTER to continue...");
Console.ReadLine();
}
}
}
0 votes
Ça sent mauvais, je suis d'accord. J'ai hâte de voir les réponses ;)
0 votes
Que diriez-vous d'attacher un gestionnaire d'événement permanent qui invoque des "gestionnaires d'événements ponctuels" ajoutés à une file d'attente ?
0 votes
Intéressant ; nous avons fait quelque chose de similaire dans PushLINQ ; mais pour la réutilisation, il y a des problèmes ; les lambdas ne peuvent pas détecter les événements, et sans l'événement, l'inférence de type est une douleur - et sans inférence de type, le gestionnaire ne peut pas être un lambda... amusant.
0 votes
Par curiosité, pourquoi ne pas utiliser un drapeau booléen variable membre qui se met en place une fois que l'événement se déclenche, puis faire "if (flag) return ;"?
0 votes
J'aurais dû ajouter plus haut : L'idée que l'expression lambda se supprime elle-même en tant que gestionnaire d'événement semble très spaghetti.
1 votes
@dtb : Cela semble être une réponse intéressante si vous pouviez démontrer en code comment cela pourrait améliorer le code que j'ai déjà. Cependant, dans de nombreux cas, je préférerais vraiment supprimer le délégué de l'événement.
0 votes
@Josh : Je suis d'accord que c'est "spaghetti-ish", donc c'est une autre raison pour avoir une solution plus ordonnée.
0 votes
@josh : Le but est de supprimer le délégué plutôt que de le voir se déclencher et ne rien faire.
0 votes
Josh, cela pourrait entraîner ce qui équivaut à une fuite de mémoire s'il le fait souvent.
0 votes
Je viens de poser une question similaire aujourd'hui : stackoverflow.com/questions/2147116/
0 votes
Il semble que cela échouerait au détachement pour les événements multicast.
0 votes
@Randolpho : Oh ? En quoi un événement "multicast" diffère-t-il d'un événement standard ? En quoi échoue-t-il ?
0 votes
@Jonathan Allen - Pouvez-vous m'indiquer un article qui explique davantage comment la fuite de mémoire peut se produire ? J'aimerais vraiment en savoir plus. La plupart de mon expérience avec les gestionnaires d'événements a été dans Windows Forms et je crée presque toujours des formulaires dans un bloc d'utilisation, donc je pensais que je n'avais pas à m'inquiéter des fuites.
0 votes
@AnthonyWJones : Un événement multicast (tout événement qui renvoie void), lorsqu'il est levé, itère sur une liste de gestionnaires d'événements souscrits et appelle chaque gestionnaire d'événement à tour de rôle - à moins que vous ne leviez spécifiquement l'événement de manière asynchrone, ce qui est en fait rare. Le fait de détacher votre gestionnaire d'événements modifie cette liste. Détacher votre gestionnaire d'événements pendant qu'il traite votre événement modifie cette liste pendant l'itération de cette liste. Et nous savons tous ce qui se passe lorsque vous modifiez une liste tout en l'itérant...
0 votes
@Randolpho : ahh, bon point, il faudra que je regarde ça.
0 votes
@Randolpho : Après avoir examiné cette question via Reflector et quelques tests, je ne peux pas générer de problèmes en utilisant cette technique. Il semblerait qu'une fois déclenché, l'ensemble actuel de délégués se déclenche indépendamment de toute suppression qui se produit à la suite de l'exécution du code dans ces délégués. Le déclenchement d'événements ultérieurs donne lieu au comportement attendu. Pour être honnête, cela ne me surprend pas, je serais plus surpris si le .NET n'avait pas anticipé ce scénario et fait des provisions pour cela.
0 votes
@AnthonyWJones : excellent point. Eh bien, si ça marche, ça marche. C'est juste qu'il me semblait que ça allait échouer à première vue :)