2010-05-26 11 views
6

Hier ist was ich meine. Angenommen, ich arbeite mit einer API, die Ereignisse verfügbar macht, aber diese Ereignisse folgen nicht der Standard-Signatur EventHandler oder EventHandler<TEventArgs>. Ein Ereignis könnte so aussehen, zum Beispiel:Wie kann ich ein IObservable <T> in Rx von einem "nicht standardmäßigen" Ereignis erhalten?

Public Event Update(ByVal sender As BaseSubscription, ByVal e As BaseEvent) 

Jetzt, in der Regel, wenn ich eine IObservable<TEventArgs> von einem Ereignis erhalten möchten, kann ich dies nur tun:

Dim updates = Observable.FromEvent(Of UpdateEventArgs)(_ 
    target:=updateSource, _ 
    eventName:="Update" _ 
) 

Aber das tut nicht Arbeit, weil das Update Ereignis ist kein EventHandler<UpdateEventArgs> - tatsächlich ist kein UpdateEventArgs - es ist im Grunde nur seine eigene Sache.

Offensichtlich konnte ich mein eigenes Klasse Ableiten von EventArgs (dh UpdateEventArgs) definieren, schreiben Sie eine andere Klasse das Objekt wickeln Sie das Update Ereignis bereitstellt, gibt der Wrapper-Klasse seinen eigenenUpdate Fall, dass ist eine EventHandler<UpdateEventArgs>, und erhalten Sie eine IObservable<UpdateEventArgs> von dass. Aber das ist eine nervige Arbeit.

Gibt es eine Möglichkeit, eine IObservable<[something]> von einem "Nicht-Standard" -Ereignis wie diesem zu erstellen, oder bin ich kein Glück?


UPDATE: obwohl

Function FromEvent(Of TDelegate, TEventArgs As EventArgs)(_ 
    conversion As Func(Of EventHandler(Of TEventArgs), TDelegate), _ 
    addHandler As Action(Of TDelegate), _ 
    removeHandler As Action(Of TDelegate) _ 
) As IObservable(Of IEvent(Of TEventArgs)) 

Ich muss zugeben, ich habe Probleme mit Verpackung: Von Jon Skeet Antwort, ich bin in Richtung der folgenden Überlastung von Observable.FromEvent stupste mein Kopf um diesen Func(Of EventHandler(Of TEventArgs), TDelegate) Teil. Es scheint mir rückwärts (?). Offensichtlich gibt es nur etwas, was mir fehlt ...

Wie auch immer, falls es hilft, ich denke, Dies ist, was die entsprechende C# -Code aussehen würde (ich bin ganz ehrlich: Ich bin mir nicht sicher . darüber Auch wenn ich es vorziehen, C selbst im allgemeinen #, ist dieser Code die Arbeit von einem meiner Kollegen, die in erster Linie in VB.NET schreiben, und ermöglicht VB.NET mehrere Syntaxen für die Deklaration von Veranstaltungen):

// notice: not an EventHandler<TEventArgs> 
public delegate void UpdateEventHandler(BaseSubscription sender, BaseEvent e); 

// not 100% sure why he did it this way 
public event UpdateEventHandler Update; 

die tricky Teil hier ist, dass es scheint, dass einige Art der Klasse, die von EventArgs abgeleitet wird, notwendig ist, egal was. In der API, mit der ich arbeite, gibt es keine solche Klasse. Also, absolutes Minimum, ich muss eins schreiben. Aber das sollte ziemlich trivial sein (im Prinzip eine Eigenschaft: BaseEvent).

Am Ende gehe ich davon aus, den Code für diese Überlastung erforderlich so etwas wie dies in C# aussehen:

var updates = Observable.FromEvent<UpdateEventHandler, UpdateEventArgs>(
    // conversion (Func<EventHandler<UpdateEventArgs>, UpdateEventHandler>) 
    handler => (sender, e) => handler(sender, new UpdateEventArgs(e)), 
    // addHandler (Action<UpdateEventHandler>) 
    handler => updateSource.Update += handler, 
    // removeHandler (Action<UpdateEventHandler>) 
    handler => updateSource.Update -= handler 
); 

Vorweg: ich habe auch diese gerade? Zweitens: Kann ich richtig sagen, dass es mit VB 9 wirklich keine Möglichkeit gibt, das oben Gesagte zu erreichen, ohne meine eigenen Methoden zu schreiben?

Es fühlt sich für mich fast so an, als würde ich dieses Problem aus dem völlig falschen Winkel heraus angehen. Aber ich bin mir wirklich nicht sicher.

+0

Ihr C# -Beispiel war korrekt, die Vorgehensweise in VB ist identisch, aber die Syntax ist etwas unordentlicher. Sehen Sie sich das Beispiel an, das ich unten für zukünftige Referenz veröffentlicht habe. –

Antwort

1

Sie kann der Lage sein, diese Signatur zu verwenden:

Public Shared Function FromEvent(Of TDelegate, TEventArgs As EventArgs) (_ 
    conversion As Func(Of EventHandler(Of TEventArgs), TDelegate), _ 
    addHandler As Action(Of TDelegate), _ 
    removeHandler As Action(Of TDelegate) _ 
) As IObservable(Of IEvent(Of TEventArgs)) 

Hier TDelegate die Delegattyp der Veranstaltung sein würde (was ich nicht sofort von Ihrer Erklärung sagen kann - C# Ereignis Erklärungen don‘ Ich sehe so aus, und ich fürchte, ich kenne nicht genug VB um es zu entziffern, aber ich bin mir sicher, dass es irgendwo einen Delegattyp gibt. TEventArgs wäre ein Typ für das Event-Argument (BaseEvent sollte es hier tun, denke ich). Sie müssten einen Konverter von EventHandler(Of BaseEvent) zu Ihrem Delegattyp bereitstellen. Dies wäre wahrscheinlich nur ein Lambda-Ausdruck, um den gegebenen Event-Handler mit den übergebenen Argumenten aufzurufen. Die Aktionen zum Hinzufügen und Entfernen sind die normalen Ereignissubskriptionscodes, die jedoch als Delegaten ausgedrückt werden.

Leider ist meine VB nicht gut genug, um all dies ordentlich auszudrücken - oder zu wissen, wie viel in VB 9 oder 10 leicht verfügbar ist. Ich weiß, wie es in C# aussehen würde ... wenn du es geben könntest mir ein kurzes, aber vollständiges Beispiel in C#, das mich nur ließ, um das Subskriptionsbit zu füllen, konnte ich das zweifellos tun ...

+0

Dies scheint definitiv eine vielversprechende Option. Ich habe meine Frage mit dem, was ich glaube * ist das C# Äquivalent des VB.NET-Code, den ich ursprünglich gepostet habe aktualisiert. Wenn Sie eine Chance bekommen, mein Update zu lesen: Sieht meine C# Version des hypothetischen Codes, den ich schreiben müsste, für Sie korrekt aus? Ich weiß, du hast gesagt, du bist nicht sehr vertraut mit VB, also denke ich, dass ich alleine bin, wenn es darum geht, diesen Teil zu erarbeiten ... –

3

Vielleicht konnten Sie gerade Ihre eigene Implementierung für die kundenspezifische Ereignisunterschrift hinzufügen?

public interface ICustomEvent<TSource, TArgs> 
{ 
    public TSource Source { get; } 
    public TArgs EventArgs { get; } 
} 

public interface CustomEvent<TSource, TArgs> : ICustomEvent<TSource, TArgs> 
{ 
    public TSource Source { get; set; } 
    public TArgs EventArgs { get; set; } 
} 

public static class ObservableEx 
{ 
    public static IObservable<ICustomEvent<TSource, TArgs>> FromCustomEvent(
     Action<Action<TSource, TArgs>> addHandler, 
     Action<Action<TSource, TArgs>> removeHandler) 
    { 
     return Observable.CreateWithDisposable(observer => 
      { 
       Action<TSource, TArgs> eventHandler = (s,a) => 
        observer.OnNext(new CustomEvent<TSource,TArgs>(s,a)); 

       addHandler(eventHandler); 

       return Disposable.Create(() => removeHandler(eventHandler)); 
      }); 
    } 
} 

Dann können Sie es als verwenden:

var observable = ObservableEx.FromCustomEvent<BaseSubscription,BaseEvent>(
    h => updateSource.Update += h, 
    h => updateSource.Update -= h 
); 
+0

Das sieht ziemlich vielversprechend aus. Ich werde das überprüfen. –

1

können Sie auch diese die faule Weise nur tun, wenn Update geht nie weg:

var observable = new Subject<BaseEvent>(); 
updateSource.Update += (o,e) => observable.OnNext(e); 

Jon Plan ist wahrscheinlich eine bessere eine, aber Themen können Ihnen helfen.

1

Für die Zukunft ist dies ein Beispiel für die Umwandlung Überlastung von FromEvent verwenden, die FileSystemEventHandler als Beispiel:

Dim createWatcher As New FileSystemWatcher With {.Path = "C:\Temp", .EnableRaisingEvents = True} 
    Dim foo = Observable.FromEvent(Of FileSystemEventHandler, FileSystemEventArgs)(
     Function(ev) New FileSystemEventHandler(Sub(o, e) ev(o, e)), 
     Sub(ev) AddHandler createWatcher.Created, ev, 
     Sub(ev) RemoveHandler createWatcher.Created, ev) 

    Dim changedEv = Observable.FromEvent(Of FileSystemEventHandler, FileSystemEventArgs)(
     Function(ev) New FileSystemEventHandler(Sub(o, e) ev(o, e)), 
     Sub(ev) AddHandler createWatcher.Changed, ev, 
     Sub(ev) RemoveHandler createWatcher.Changed, ev) 

    foo.Subscribe(Sub(e) Console.WriteLine("File {0} created.", e.EventArgs.Name)) 
    changedEv.Subscribe(Sub(e) Console.WriteLine("File {0} changed.", e.EventArgs.Name)) 
0
var updates = Observable.FromEvent<UpdateEventHandler, UpdateEventArgs>(
    // conversion (Func<EventHandler<UpdateEventArgs>, UpdateEventHandler>) 
    handler => (BaseSubscription sender, BaseEvent e) => handler.Invoke(sender, new UpdateEventArgs(e)), 
    // addHandler (Action<UpdateEventHandler>) 
    handler => updateSource.Update += handler, 
    // removeHandler (Action<UpdateEventHandler>) 
    handler => updateSource.Update -= handler 
); 

Stellen Sie sicher, UpdateEventArgs Ctor ein Baseevent Argument akzeptiert.

0

Ich habe einen ähnlichen Albtraum, weil ich mit einer Interop-API arbeite, die nicht standardmäßige .net-Ereignisse verwendet. Ich bin ein Anfänger in vielen Dingen einschließlich Generics, Funcs, Actions, Observables und Rx, also glaube ich, dass meine Erfahrung, wie man solche Dinge versteht, von etwas Wert sein wird.

Wir können Ihren Kopf um Func(Of EventHandler(Of TEventArgs), TDelegate) conversion bekommen, indem Sie verstehen, wo es verwendet wird.

Aber zuerst müssen wir die Generikasignatur der FromEvent Methode verstehen.

Nachfolgend finden Sie Funktionssignatur des FromEvent Erweiterungsmethode in vb:

Function FromEvent(Of TDelegate, TEventArgs As EventArgs)(_ 
    conversion As Func(Of EventHandler(Of TEventArgs), TDelegate), _ 
    addHandler As Action(Of TDelegate), _ 
    removeHandler As Action(Of TDelegate) _ 
) As IObservable(Of IEvent(Of TEventArgs)) 

Aber ich benutze vb nicht wirklich so ich auf die C# Unterschrift zurückgreifen müssen:

IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(
    Func<Action<TEventArgs>, TDelegate> conversion, 
    Action<TDelegate> addHandler, 
    Action<TDelegate> removeHandler); 

Lassen Sie uns analysieren die C# -Signatur Zeile für Zeile.

Hinweis: Es wird Zeiten geben, in denen ich Datentypen in Lambda-Ausdrücke aufnehmen werde, um standardmäßige .net-Ereignisse von nicht standardmäßigen Ereignissen zu unterscheiden.

Erster Teil:

IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(

Diese besagt, dass Funktion FromEvent eine TDelegate und TEventArgs As EventArgs als Eingabe akzeptiert. Beachten Sie, dass der OUTPUT IObservable auch den Typ TEventArgs hat. Sie haben also Recht, als Sie sagten, dass Sie eine Klasse benötigen, die die Daten von TDelegate umschließt. Ich weiß nicht, welche Version ich verwende, aber ich kann jede Klasse verwenden, auch wenn sie nicht von EventArgs erbt. Falls vb das nicht zulässt, ist es trotzdem trivial, da nur an die Klasse angehängt wird (Inherits from vb?). Lassen Sie uns dies auf Ihr Problem:

Ihre C# Annahmen:

// notice: not an EventHandler<TEventArgs> public delegate void 
public delegate void UpdateEventHandler(BaseSubscription sender, BaseEvent e); 

// class to wrap the data from the above delegate 
public class UpdateEventArgs:EventArgs {...} 

auf die erste Zeile anwenden wird:

var updates = FromEvent<UpdateEventHandler, UpdateEventArgs>(

Zweiter Teil:

Als nächstes haben wir drei Eingänge conversion, addhandler und removehandler:

Wir wissen, dass addHandler und removeHandler nur hinzugefügt und entfernt den Delegaten von einem Ereignis. Lass uns diese zwei zuerst machen.

// addHandler (Action<UpdateEventHandler>) 
handler => updateSource.Update += handler, 
// removeHandler (Action<UpdateEventHandler>) 
handler => updateSource.Update -= handler 

Jetzt können unsere Art auf die schwierige Teil gelten:

Func<Action<UpdateEventArgs>, UpdateEventHandler> conversion, 

Diese Funktion eine Action<UpdateEventArgs> als Eingabe und ein UpdateEventHandler Delegierten ist der Ausgang. Lassen Sie uns weisen Sie in einer Variablen namens conversion

Func<Action<UpdateEventArgs>, UpdateEventHandler> conversion 
    = (handlerOfUpdateEventArgs) => 
    { 
     UpdateEventHandler handler = (BaseSubscription sender, BaseEvent e) => 
      handlerOfUpdateEventArgs(sender, new UpdateEventArgs(e)); 
     return handler; 
    }; 

besser zu verstehen, was das ist, schauen wir uns an, wie wir einen Event-Handler zu einem Standard .net Ereignis zuordnen:

someObject.SomeEvent += (object sender,EventArgs args) => { ... }; 

Jetzt schauen wir auf Ihre nicht-Standard-.net-Ereignis und UpdateEventHandler:

public delegate void UpdateEventHandler(BaseSubscription sender, BaseEvent e); 
updateSource.Update += (BaseSubscription sender, BaseEvent e) => { ... }; 

Wenn Sie bei der Unterzeichnung von conversion Funktion zurückblicken, es gibt ein UpdateEventHandler Delegierter. Dies würde bedeuten, dass wir conversion verwenden können, um an das Ereignis Update anzuhängen. Aber bevor wir das tun können, benötigt die conversion Funktion eine Action<UpdateEventArgs> als Eingabe, bevor sie verwendet werden kann. Lassen Sie uns das jetzt tun:

//EventHandler<EventArgs> similarity. 
Action<UpdateEventArgs> actionUpdateEventArgs = (UpdateEventArgs e) => 
    { 
     //This is were you put your code like in a standard.net event 
     //This is also probably where the Observable.FromEvent() puts 
     //wiring necessary to make it into an IObservable<UpdateEventArgs> 
    }; 

Jetzt, da wir alle Teile haben, die wir brauchen, wir conversion ähnlich einem Ereignishandler verwenden können.

updateSource.Update += conversion(actionUpdateEventArgs); 

-Code innerhalb actionUpdateEventArgs wird jedes Mal Update angehoben aufgerufen werden.

Hoffentlich war das genug Erklärung, um den Parameter Func<Action<UpdateEventArgs>, UpdateEventHandler> conversion zu verstehen.

Schließlich ist dies, wie Sie die FromEvent() Methode verwenden werden:

Func<Action<UpdateEventArgs>, UpdateEventHandler> conversion 
    = (handlerOfUpdateEventArgs) => 
    { 
     UpdateEventHandler handler = (BaseSubscription sender, BaseEvent e) => 
      handlerOfUpdateEventArgs(sender, new UpdateEventArgs(e)); 
     return handler; 
    }; 

var updates = Observable.FromEvent<UpdateEventHandler, UpdateEventArgs>(
    // conversion (Func<EventHandler<UpdateEventArgs>, UpdateEventHandler>) 
    conversion, 
    // addHandler (Action<UpdateEventHandler>) 
    handler => updateSource.Update += handler, 
    // removeHandler (Action<UpdateEventHandler>) 
    handler => updateSource.Update -= handler 
); 

Dies ist, wie ich es verstanden eine newbies Sicht bilden, so ich hoffe, es wird hilfreich sein.

Verwandte Themen