Utilisation du nouveau modèle ASYNC / AWAIT, il est assez simple de générer une tâche code> code> terminée lorsqu'un événement déclenche; Il vous suffit de suivre ce modèle:
public static Task FromEvent<T>(this T obj, string eventName) { var tcs = new TaskCompletionSource<object>(); var eventInfo = obj.GetType().GetEvent(eventName); Type eventDelegate = eventInfo.EventHandlerType; Type[] parameterTypes = GetDelegateParameterTypes(eventDelegate); DynamicMethod handler = new DynamicMethod("unnamed", null, parameterTypes); ILGenerator ilgen = handler.GetILGenerator(); //TODO ilgen.Emit calls go here Delegate dEmitted = handler.CreateDelegate(eventDelegate); eventInfo.AddEventHandler(obj, dEmitted); return tcs.Task; }
4 Réponses :
Si vous êtes prêt à avoir une méthode par type de délégué, vous pouvez faire quelque chose comme: vous l'utiliseriez comme: p> string s = await FromEvent<string>(x => c.OnCompletion += x);
Le principal problème est que de nombreux cadres d'interface utilisateur créent leur propre type de délégué pour chaque événement (plutôt que d'utiliser action
EventHandler
fromevent code> pour chaque type de délégué serait mieux i>, mais toujours pas parfait. Cela dit, vous pourriez simplement avoir la première méthode que vous avez faite et utilisée:
attendre devent (x => nouveau myclass (). Oncompletion + = (A, B) => x ((code> sur n'importe quel un événement. C'est une sorte de solution à mi-chemin.
@Servy ouais, j'ai pensé à le faire de cette façon aussi, mais je ne l'ai pas mentionné parce que je pense que c'est laid (i. Trop de chaudières).
Cela vous donnera ce dont vous avez besoin sans avoir besoin de faire d'ilgen, et de plus simple. Cela fonctionne avec n'importe quel type de délégués d'événements; Il vous suffit de créer un gestionnaire différent pour chaque nombre de paramètres dans votre délégué d'événement. Vous trouverez ci-dessous les gestionnaires dont vous auriez besoin de 0..2, ce qui devrait être la grande majorité de vos cas d'utilisation. S'étendant à 3 et plus est une copie simple et une pâte de la méthode de 2 paramètres.
Ceci est également plus puissant que la méthode Ilgen, car vous pouvez utiliser toutes les valeurs créées par l'événement de votre motif asynchronisé. P>
static async void Run() { var result = await TaskFromEvent<int, string>(new MyClass(), "Fired"); Console.WriteLine(result); // (123, "abcd") } public class MyClass { public delegate void TwoThings(int x, string y); public MyClass() { new Thread(() => { Thread.Sleep(1000); Fired(123, "abcd"); }).Start(); } public event TwoThings Fired; }
Thassk beaucoup !!! Pour Windows Phone, cette ligne doit être modifiée: var paramètres = méthodyinfo.getparameterers (). Sélectionnez (A => System.Linq.Expressions.expression.paramètre (A.ParameTTyètre (A.ParametType, A.Name))). TOARRAY ();
Vous allez ici:
static async void Run() { object[] result = await new MyClass().FromEvent("Fired"); Console.WriteLine(string.Join(", ", result.Select(arg => arg.ToString()).ToArray())); // 123, abcd } public class MyClass { public delegate void TwoThings(int x, string y); public MyClass() { new Thread(() => { Thread.Sleep(1000); Fired(123, "abcd"); }).Start(); } public event TwoThings Fired; }
Je viens de finir de regarder par-dessus votre mise à jour et de jouer un peu. J'aime vraiment ça. Le gestionnaire d'événements est désabonné, ce qui est une grande touche. Les différents gestionnaires d'événements sont mis en cache, donc il n'est pas généré à plusieurs reprises pour les mêmes types et, contrairement à d'autres solutions, il n'est pas nécessaire de spécifier les types d'arguments au gestionnaire d'événements.
Je ne pouvais pas faire fonctionner le code sur Windows Phone, je ne sais pas si c'est une question de sécurité. Mais non travaillé .. Exception: {"Tentative d'accès à la méthode a échoué: System.Reflection.emit.dynamicmethod..ctor (system.type, system.type, system.type [], system.type)"}
@ J.Lennon Malheureusement, je ne suis pas capable de le tester sur le téléphone Windows. Donc, je serai vraiment reconnaissant si vous pouviez essayer d'utiliser ce Version mise à jour B> et laissez-moi savoir si cela aide. Merci d'avance.
@ J.Lennon Je pense que Servy a couvert que dans son commentaire ultérieur. Il existe probablement des différences de performance également (aucune idée qui serait plus rapide dans laquelle des scénarios sans profilage), mais des abonnements d'événements ou des déclencheurs sont peu susceptibles d'être un goulot d'étranglement de toute façon.
Savez-vous s'il serait possible de simplifier cela avec des arbres d'expression ou avez-vous besoin de l'ilgen de bas niveau?
J'ai fait face à ce problème en essayant d'écrire utilisation: p> contrecatawaiter code> méthode d'extension pour
system.action code>, oubliant que
system.action code> est immutable et en passant comme argument, vous faites une copie. Cependant, vous ne faites pas une copie si vous le transmettez avec
ref code> mot-clé, donc:
Cette méthode ne peut pas être utilisée pour attendre des événements d'autres classes. Il ne peut être utilisé qu'à partir de la classe actuelle. Sinon, vous obtiendrez une erreur de compilation: l'événement 'MyClass.OntionFinished' ne peut apparaître que sur le côté gauche de + = ou - = (sauf lorsqu'il est utilisé à partir de l'intérieur de type 'mycass') i >
Malheureusement donc. Ajout d'une ligne de code autreclass.onaction + = () => OnactionFinished? .Invoke (); code> avant
Await code> était pratique dans mon cas.
Notez que le BCL a
TaskFactory.fromasync code> pour traduire facilement de APM en appuyez sur. Il n'y a pas de manière facile et i> générique de traduire du PAE à Tap, alors je pense que c'est pourquoi MM n'a pas inclus une solution comme celle-ci. Je trouve RX (ou TPL Dataflow) pour être une correspondance plus proche de la sémantique "événement" de toute façon - et RX fait i> a un
geventant code> type de méthode.
J'ai aussi voulu faire un
générique <> code>, et Ce est proche comme je pouvais arriver à cela sans utiliser de réflexion.