Warum werden Themen in .NET Reactive Extensions nicht empfohlen?


110

Ich bin derzeit mit dem Reactive Extensions-Framework für .NET vertraut und arbeite mich durch die verschiedenen Einführungsressourcen, die ich gefunden habe (hauptsächlich http://www.introtorx.com ).

Unsere Anwendung umfasst eine Reihe von Hardwareschnittstellen, die Netzwerkrahmen erkennen. Dies sind meine IObservables. Ich habe dann eine Vielzahl von Komponenten, die diese Rahmen verbrauchen oder eine Art Transformation für die Daten durchführen und einen neuen Rahmentyp erzeugen. Es wird auch andere Komponenten geben, die beispielsweise jeden n-ten Frame anzeigen müssen. Ich bin überzeugt, dass Rx für unsere Anwendung nützlich sein wird, habe jedoch Probleme mit den Implementierungsdetails für die IObserver-Schnittstelle.

Die meisten (wenn nicht alle) Ressourcen, die ich gelesen habe, haben gesagt, dass ich die IObservable-Schnittstelle nicht selbst implementieren sollte, sondern eine der bereitgestellten Funktionen oder Klassen verwenden sollte. Aus meiner Forschung geht hervor, dass das Erstellen eines Subject<IBaseFrame>mir das bietet, was ich brauche. Ich hätte meinen einzelnen Thread, der Daten von der Hardwareschnittstelle liest und dann die OnNext-Funktion meiner Subject<IBaseFrame>Instanz aufruft . Die verschiedenen IObserver-Komponenten würden dann ihre Benachrichtigungen von diesem Betreff erhalten.

Meine Verwirrung ergibt sich aus den Ratschlägen im Anhang dieses Tutorials, in denen es heißt:

Vermeiden Sie die Verwendung der Betreffarten. Rx ist effektiv ein funktionales Programmierparadigma. Die Verwendung von Subjekten bedeutet, dass wir jetzt den Status verwalten, der möglicherweise mutiert. Es ist sehr schwierig, sich gleichzeitig mit dem Mutationsstatus und der asynchronen Programmierung zu befassen. Darüber hinaus wurden viele der Operatoren (Erweiterungsmethoden) sorgfältig geschrieben, um sicherzustellen, dass die korrekte und konsistente Lebensdauer von Abonnements und Sequenzen erhalten bleibt. Wenn Sie Themen einführen, können Sie dies unterbrechen. In zukünftigen Versionen kann es auch zu erheblichen Leistungseinbußen kommen, wenn Sie explizit Themen verwenden.

Meine Anwendung ist sehr leistungskritisch. Ich werde natürlich die Leistung der Verwendung der Empfangsmuster testen, bevor sie in den Produktionscode eingeht. Ich mache mir jedoch Sorgen, dass ich mit der Subject-Klasse etwas tue, das gegen den Geist des Rx-Frameworks verstößt, und dass eine zukünftige Version des Frameworks die Leistung beeinträchtigen wird.

Gibt es eine bessere Möglichkeit, das zu tun, was ich will? Der Hardware-Polling-Thread wird kontinuierlich ausgeführt, unabhängig davon, ob Beobachter vorhanden sind oder nicht (der HW-Puffer wird ansonsten gesichert). Dies ist also eine sehr heiße Sequenz. Ich muss dann die empfangenen Frames an mehrere Beobachter weitergeben.

Jeder Rat wäre sehr dankbar.


1
Es hat mir wirklich geholfen, das Thema zu verstehen. Ich habe gerade klargestellt, wie ich es in meiner Anwendung verwenden soll. Ich weiß, dass sie das Richtige sind - ich habe eine Pipeline von Komponenten, die sehr Push-orientiert sind, und ich muss alle Arten von Filtern und Aufrufen des UI-Threads durchführen, um sie in einer GUI anzuzeigen sowie den zuletzt empfangenen Frame usw. Zu puffern etc - Ich muss nur sicherstellen, dass ich es beim ersten Mal richtig mache!
Anthony

Antworten:


70

Ok, wenn wir meine dogmatischen Wege ignorieren und "Themen sind gut / schlecht" alle zusammen ignorieren. Schauen wir uns den Problembereich an.

Ich wette, Sie haben entweder 1 von 2 Systemstilen, in die Sie sich integrieren müssen.

  1. Das System löst ein Ereignis oder einen Rückruf aus, wenn eine Nachricht eintrifft
  2. Sie müssen das System abfragen, um festzustellen, ob eine Nachricht verarbeitet werden muss

Für Option 1, einfach, wickeln wir es einfach mit der entsprechenden FromEvent-Methode ein und wir sind fertig. Zum Pub!

Für Option 2 müssen wir nun überlegen, wie wir dies abfragen und wie dies effizient durchgeführt werden kann. Wie veröffentlichen wir den Wert, wenn wir ihn erhalten?

Ich würde mir vorstellen, dass Sie einen dedizierten Thread für die Abfrage wünschen. Sie möchten nicht, dass ein anderer Codierer den ThreadPool / TaskPool hämmert und Sie in einer ThreadPool-Hungersituation zurücklässt. Alternativ möchten Sie nicht den Aufwand des Kontextwechsels (ich denke). Nehmen wir also an, wir haben unseren eigenen Thread, wir werden wahrscheinlich eine Art While / Sleep-Schleife haben, in der wir sitzen, um abzufragen. Wenn die Prüfung einige Nachrichten findet, veröffentlichen wir sie. Nun, all dies klingt perfekt für Observable.Create. Jetzt können wir wahrscheinlich keine While-Schleife verwenden, da wir dadurch niemals ein Einwegprodukt zurückgeben können, um die Stornierung zu ermöglichen. Zum Glück haben Sie das ganze Buch gelesen und sind mit der rekursiven Planung vertraut!

Ich stelle mir vor, so etwas könnte funktionieren. #Nicht getestet

public class MessageListener
{
    private readonly IObservable<IMessage> _messages;
    private readonly IScheduler _scheduler;

    public MessageListener()
    {
        _scheduler = new EventLoopScheduler();

        var messages = ListenToMessages()
                                    .SubscribeOn(_scheduler)
                                    .Publish();

        _messages = messages;
        messages.Connect();
    }

    public IObservable<IMessage> Messages
    {
        get {return _messages;}
    }

    private IObservable<IMessage> ListenToMessages()
    {
        return Observable.Create<IMessage>(o=>
        {
                return _scheduler.Schedule(recurse=>
                {
                    try
                    {           
                        var messages = GetMessages();
                        foreach (var msg in messages)
                        {
                            o.OnNext(msg);
                        }   
                        recurse();
                    }
                    catch (Exception ex)
                    {
                        o.OnError(ex);
                    }                   
                });
        });
    }

    private IEnumerable<IMessage> GetMessages()
    {
         //Do some work here that gets messages from a queue, 
         // file system, database or other system that cant push 
         // new data at us.
         // 
         //This may return an empty result when no new data is found.
    }
}

Der Grund, warum ich Themen wirklich nicht mag, ist, dass der Entwickler normalerweise kein klares Design für das Problem hat. Hacken Sie ein Thema ein, stecken Sie es hier und überall hin und lassen Sie dann den armen Support-Entwickler bei WTF raten. Wenn Sie die Methoden Create / Generate usw. verwenden, lokalisieren Sie die Auswirkungen auf die Sequenz. Sie können alles auf eine Weise sehen und Sie wissen, dass niemand sonst eine böse Nebenwirkung hat. Wenn ich ein Fachfeld sehe, muss ich jetzt nach allen Stellen in einer Klasse suchen, die es verwendet. Wenn ein MFer einen öffentlich macht, sind alle Wetten ungültig, wer weiß, wie diese Sequenz verwendet wird! Async / Concurrency / Rx ist schwierig. Sie müssen es nicht schwieriger machen, indem Sie zulassen, dass Nebenwirkungen und Kausalitätsprogrammierung Ihren Kopf noch mehr drehen.


10
Ich lese gerade diese Antwort, aber ich hatte das Gefühl, ich sollte darauf hinweisen, dass ich niemals in Betracht ziehen würde, die Betreff-Oberfläche freizulegen! Ich verwende es, um die IObservable <> -Implementierung innerhalb einer versiegelten Klasse bereitzustellen (die das IObservable <> verfügbar macht). Ich kann definitiv sehen, warum das Belichten der Subject <> -Schnittstelle eine schlechte Sache wäre
Anthony

Hey, tut mir leid, dass ich dick bin, aber ich verstehe deinen Code einfach nicht wirklich. Was machen ListenToMessages () und GetMessages () und kehren zurück?
user10479

1
Für Ihr persönliches Projekt @jeromerg kann dies in Ordnung sein. Nach meiner Erfahrung können Entwickler jedoch Probleme mit WPF, MVVM haben. Das Testen des GUI-Designs und das anschließende Einfügen von Rx können die Dinge komplizierter machen. Ich habe das BehaviourSubject-as-a-Property-Muster ausprobiert. Ich fand jedoch, dass es für andere viel besser geeignet ist, wenn wir Standard-INPC-Eigenschaften verwenden und dann eine einfache Erweiterungsmethode verwenden, um diese in IObservable zu konvertieren. Darüber hinaus benötigen Sie benutzerdefinierte WPF-Bindungen, um mit Ihren Verhaltensthemen arbeiten zu können. Jetzt muss Ihr armes Team auch WPF, MVVM, Rx und Ihr neues Framework lernen.
Lee Campbell

2
@LeeCampbell, um es in Bezug auf Ihr Codebeispiel auszudrücken, der normale Weg wäre, dass MessageListener vom System erstellt wird (Sie registrieren wahrscheinlich den Klassennamen irgendwie), und Ihnen wird gesagt, dass das System dann OnCreate () und aufruft OnGoodbye () und ruft beim Generieren von Nachrichten message1 (), message2 () und message3 () auf. Es scheint, als würde messageX [123] OnNext zu einem Thema aufrufen, aber gibt es einen besseren Weg?
James Moore

1
@ JamesMoore, da diese Dinge mit konkreten Beispielen viel einfacher zu erklären sind. Wenn Sie eine Open Source Android-App kennen, die Rx und Subjects verwendet, kann ich vielleicht Zeit finden, um zu sehen, ob ich einen besseren Weg bieten kann. Ich verstehe, dass es nicht sehr hilfreich ist, auf einem Podest zu stehen und zu sagen, dass die Themen schlecht sind. Aber ich denke, Dinge wie IntroToRx, RxCookbook und ReactiveTrader geben verschiedene Beispiele für die Verwendung von Rx.
Lee Campbell

38

Im Allgemeinen sollten Sie die Verwendung vermeiden Subject, aber für das, was Sie hier tun, denke ich, dass sie recht gut funktionieren. Ich habe eine ähnliche Frage gestellt, als ich in Rx-Tutorials auf die Meldung "Themen vermeiden" stieß.

Um Dave Sexton (von Rxx) zu zitieren

"Subjekte sind die statusbehafteten Komponenten von Rx. Sie sind nützlich, wenn Sie ein ereignisähnliches Observable als Feld oder lokale Variable erstellen müssen."

Ich neige dazu, sie als Einstiegspunkt in Rx zu verwenden. Wenn ich also einen Code habe, der sagen muss, dass etwas passiert ist (wie Sie), würde ich a verwenden Subjectund anrufen OnNext. Stellen Sie dies dann IObservablezur Verfügung, damit andere es abonnieren können (Sie können es AsObservable()für Ihr Thema verwenden, um sicherzustellen, dass niemand ein Thema bearbeiten und die Dinge durcheinander bringen kann).

Sie können dies auch mit einem .NET-Ereignis erreichen und verwenden FromEventPattern. Wenn ich das Ereignis jedoch nur in ein Ereignis verwandeln IObservablemöchte, sehe ich keinen Vorteil darin, ein Ereignis anstelle eines Ereignisses zu haben Subject(was bedeuten könnte, dass ich vermisse etwas hier)

Was Sie jedoch unbedingt vermeiden sollten, ist das Abonnieren von a IObservablemit a Subject, dh nicht das Übergeben von a Subjectan die IObservable.SubscribeMethode.


Warum brauchst du überhaupt Staat? Wie meine Antwort zeigt, müssen Sie den Status nicht wirklich verwalten, wenn Sie das Problem in einzelne Teile zerlegen. Themen sollten in diesem Fall nicht verwendet werden.
CasperOne

8
@casperOne Sie benötigen keinen Status außerhalb des Betreffs <T> oder des Ereignisses (beide haben Sammlungen von Dingen, die aufgerufen werden müssen, Beobachter oder Ereignishandler). Ich bevorzuge es einfach, einen Betreff zu verwenden, wenn der einzige Grund, ein Ereignis hinzuzufügen, darin besteht, es mit FromEventPattern zu verpacken. Abgesehen von einer Änderung der Ausnahmeschemata, die für Sie wichtig sein könnte, sehe ich keinen Vorteil darin, das Thema auf diese Weise zu vermeiden. Auch hier könnte mir etwas anderes fehlen, das das Ereignis dem Betreff vorzuziehen hat. Die Erwähnung des Staates war nur ein Teil des Zitats, und es schien besser, es zu belassen. Vielleicht ist es ohne diesen Teil klarer?
Wilka

@casperOne - aber Sie sollten auch kein Ereignis erstellen, um es mit FromEventPattern zu verpacken. Das ist offensichtlich eine schreckliche Idee.
James Moore

3
Ich habe mein Zitat in diesem Blog-Beitrag ausführlicher erklärt .
Dave Sexton

Ich neige dazu, sie als Einstiegspunkt in Rx zu verwenden. Das traf mich auf den Kopf. Ich habe eine Situation, in der es eine API gibt, die beim Aufrufen Ereignisse generiert, die ich durch eine reaktive Verarbeitungspipeline leiten möchte. Das Thema war die Antwort für mich, da das FromEventPattern in RxJava AFAICT nicht zu existieren scheint.
Scorpiodawg

31

Wenn Sie einen Betreff verwalten, implementieren Sie häufig nur Funktionen, die bereits in Rx vorhanden sind, und wahrscheinlich nicht so robust, einfach und erweiterbar.

Wenn Sie versuchen, einen asynchronen Datenfluss in Rx anzupassen (oder einen asynchronen Datenfluss aus einem zu erstellen, der derzeit nicht asynchron ist), sind die häufigsten Fälle normalerweise:

  • Die Datenquelle ist ein Ereignis : Wie Lee sagt, ist dies der einfachste Fall: Verwenden Sie FromEvent und gehen Sie in die Kneipe.

  • Die Datenquelle ist ein synchroner Vorgang, und Sie möchten abgefragte Aktualisierungen (z. B. einen Webservice oder einen Datenbankaufruf): In diesem Fall können Sie den von Lee vorgeschlagenen Ansatz verwenden, oder in einfachen Fällen können Sie so etwas wie verwenden Observable.Interval.Select(_ => <db fetch>). Möglicherweise möchten Sie DistinctUntilChanged () verwenden, um das Veröffentlichen von Updates zu verhindern, wenn sich an den Quelldaten nichts geändert hat.

  • Die Datenquelle ist eine Art asynchrone API, die Ihren Rückruf aufruft : Verwenden Sie in diesem Fall Observable.Create, um Ihren Rückruf zu verbinden und OnNext / OnError / OnComplete für den Beobachter aufzurufen.

  • Die Datenquelle ist ein Aufruf, der blockiert, bis neue Daten verfügbar sind (z. B. einige synchrone Socket-Lesevorgänge): In diesem Fall können Sie Observable.Create verwenden, um den imperativen Code zu verpacken, der aus dem Socket liest und auf Observer.OnNext veröffentlicht wenn Daten gelesen werden. Dies ähnelt möglicherweise dem, was Sie mit dem Betreff tun.

Die Verwendung von Observable.Create im Vergleich zum Erstellen einer Klasse, die einen Betreff verwaltet, entspricht in etwa der Verwendung des Schlüsselworts yield im Vergleich zum Erstellen einer ganzen Klasse, die IEnumerator implementiert. Natürlich können Sie einen IEnumerator schreiben, der so sauber und bürgerlich ist wie der Ertragscode, aber welcher ist besser gekapselt und fühlt sich ordentlicher an? Gleiches gilt für Observable.Create vs. Managing Subjects.

Observable.Create bietet Ihnen ein sauberes Muster für die verzögerte Einrichtung und den sauberen Abbau. Wie erreichen Sie dies mit einer Klasse, die ein Thema umschließt? Sie benötigen eine Art Start-Methode ... woher wissen Sie, wann Sie sie aufrufen müssen? Oder starten Sie es einfach immer, auch wenn niemand zuhört? Und wenn Sie fertig sind, wie können Sie es dazu bringen, nicht mehr aus dem Socket zu lesen / die Datenbank abzufragen usw.? Sie müssen über eine Art Stop-Methode verfügen und weiterhin Zugriff nicht nur auf das IObservable haben, das Sie abonniert haben, sondern auch auf die Klasse, die den Betreff erstellt hat.

Mit Observable.Create ist alles an einem Ort zusammengefasst. Der Hauptteil von Observable.Create wird erst ausgeführt, wenn jemand ihn abonniert hat. Wenn also niemand abonniert, verwenden Sie Ihre Ressource niemals. Und Observable.Create gibt ein Disposable zurück, mit dem Ihre Ressourcen / Rückrufe usw. sauber heruntergefahren werden können. Dies wird aufgerufen, wenn der Observer sich abmeldet. Die Lebensdauer der Ressourcen, die Sie zum Generieren des Observable verwenden, hängt eng mit der Lebensdauer des Observable selbst zusammen.


1
Sehr klare Erklärung von Observable.Create. Danke dir!
Evan Moran

1
Ich habe immer noch Fälle, in denen ich ein Subjekt verwende, in denen ein Brokerobjekt das Beobachtbare verfügbar macht (sagen wir, es ist nur eine veränderbare Eigenschaft). Verschiedene Komponenten rufen den Broker auf, wenn sich diese Eigenschaft ändert (mit einem Methodenaufruf), und diese Methode führt einen OnNext durch. Verbraucher abonnieren. Ich denke, ich würde in diesem Fall ein BehaviorSubject verwenden. Ist das angemessen?
Frank Schwieterman

1
Es hängt von der Situation ab. Gutes Rx-Design tendiert dazu, das System in eine asynchrone / reaktive Architektur umzuwandeln. Es kann schwierig sein, kleine Komponenten des reaktiven Codes sauber in ein System zu integrieren, das unbedingt erforderlich ist. Die Pflasterlösung besteht darin, Subjekte zu verwenden, um zwingende Aktionen (Funktionsaufrufe, Eigenschaftssätze) in beobachtbare Ereignisse umzuwandeln. Dann haben Sie kleine Taschen mit reaktivem Code und kein echtes "Aha!" Moment. Wenn Sie das Design ändern, um den Datenfluss zu modellieren und darauf zu reagieren, erhalten Sie normalerweise ein besseres Design. Dies ist jedoch eine allgegenwärtige Änderung und erfordert eine Änderung der Denkweise und ein Team-Buy-In.
Niall Connaughton

1
Ich würde hier sagen (als Rx unerfahren), dass: Durch die Verwendung von Subjekten können Sie die Welt von Rx innerhalb einer erweiterten imperativen Anwendung betreten und sie langsam transformieren. Auch um erste Erfahrungen zu sammeln .... und sicherlich später Ihren Code so zu ändern, wie er von Anfang an hätte sein sollen (lol). Aber zunächst denke ich, dass es sich lohnen könnte, Themen zu verwenden.
Robetto

9

Der zitierte Blocktext erklärt ziemlich genau, warum Sie ihn nicht verwenden sollten Subject<T>, aber um es einfacher auszudrücken, Sie kombinieren die Funktionen von Beobachter und Beobachtbarem, während Sie dazwischen einen Zustand einfügen (unabhängig davon, ob Sie kapseln oder erweitern).

Hier geraten Sie in Schwierigkeiten. Diese Verantwortlichkeiten sollten getrennt und voneinander getrennt sein.

In Ihrem speziellen Fall würde ich Ihnen jedoch empfehlen, Ihre Bedenken in kleinere Teile aufzuteilen.

Erstens haben Sie Ihren Thread, der heiß ist, und überwachen die Hardware immer auf Signale, für die Benachrichtigungen ausgelöst werden sollen. Wie würden Sie das normalerweise machen? Ereignisse . Beginnen wir also damit.

Definieren wir, EventArgsdass Ihr Ereignis ausgelöst wird.

// The event args that has the information.
public class BaseFrameEventArgs : EventArgs
{
    public BaseFrameEventArgs(IBaseFrame baseFrame)
    {
        // Validate parameters.
        if (baseFrame == null) throw new ArgumentNullException("IBaseFrame");

        // Set values.
        BaseFrame = baseFrame;
    }

    // Poor man's immutability.
    public IBaseFrame BaseFrame { get; private set; }
}

Nun die Klasse, die das Ereignis auslösen wird. Beachten Sie , könnte dies eine statische Klasse sein (da Sie immer ein Thread läuft die Hardware - Pufferüberwachung), oder etwas , das Sie rufen On-Demand , die abonniert hat, dass . Sie müssen dies entsprechend ändern.

public class BaseFrameMonitor
{
    // You want to make this access thread safe
    public event EventHandler<BaseFrameEventArgs> HardwareEvent;

    public BaseFrameMonitor()
    {
        // Create/subscribe to your thread that
        // drains hardware signals.
    }
}

Jetzt haben Sie eine Klasse, die ein Ereignis verfügbar macht. Observables funktionieren gut mit Ereignissen. So sehr, dass es erstklassige Unterstützung für die Konvertierung von Ereignisströmen (stellen Sie sich einen Ereignisstrom als mehrere Auslöser eines Ereignisses vor) in IObservable<T>Implementierungen gibt, wenn Sie dem Standardereignismuster folgen, und zwar über die statische FromEventPatternMethode für die ObservableKlasse .

Mit der Quelle Ihrer Ereignisse und der FromEventPatternMethode können wir auf IObservable<EventPattern<BaseFrameEventArgs>>einfache Weise eine erstellen (die EventPattern<TEventArgs>Klasse verkörpert, was Sie in einem .NET-Ereignis sehen würden, insbesondere eine Instanz, die von EventArgseinem Absender abgeleitet ist, und ein Objekt, das den Absender darstellt):

// The event source.
// Or you might not need this if your class is static and exposes
// the event as a static event.
var source = new BaseFrameMonitor();

// Create the observable.  It's going to be hot
// as the events are hot.
IObservable<EventPattern<BaseFrameEventArgs>> observable = Observable.
    FromEventPattern<BaseFrameEventArgs>(
        h => source.HardwareEvent += h,
        h => source.HardwareEvent -= h);

Natürlich möchten Sie eine IObservable<IBaseFrame>, aber das ist einfach, indem Sie die SelectErweiterungsmethode für die ObservableKlasse verwenden, um eine Projektion zu erstellen (genau wie in LINQ, und wir können all dies in einer benutzerfreundlichen Methode zusammenfassen):

public IObservable<IBaseFrame> CreateHardwareObservable()
{
    // The event source.
    // Or you might not need this if your class is static and exposes
    // the event as a static event.
    var source = new BaseFrameMonitor();

    // Create the observable.  It's going to be hot
    // as the events are hot.
    IObservable<EventPattern<BaseFrameEventArgs>> observable = Observable.
        FromEventPattern<BaseFrameEventArgs>(
            h => source.HardwareEvent += h,
            h => source.HardwareEvent -= h);

    // Return the observable, but projected.
    return observable.Select(i => i.EventArgs.BaseFrame);
}

7
Vielen Dank für Ihre Antwort @casperOne, dies war mein erster Ansatz, aber es fühlte sich "falsch" an, ein Ereignis hinzuzufügen, nur damit ich es mit Rx abschließen konnte. Ich verwende derzeit Delegaten (und ja, ich weiß, dass dies genau ein Ereignis ist!), Um in den Code zu passen, der zum Laden und Speichern der Konfiguration verwendet wird. Dies muss in der Lage sein, die Komponenten-Pipelines neu zu erstellen, und das Delegatensystem hat mir am meisten gegeben Flexibilität. Rx bereitet mir jetzt in diesem Bereich Kopfschmerzen, aber die Leistung von allem anderen im Framework macht die Lösung des Konfigurationsproblems sehr lohnenswert.
Anthony

@Anthony Wenn Sie sein Codebeispiel zum Laufen bringen können, großartig, aber wie ich kommentiert habe, macht es keinen Sinn. Was das Gefühl "falsch" betrifft, weiß ich nicht, warum die Unterteilung von Dingen in logische Teile "falsch" erscheint, aber Sie haben in Ihrem ursprünglichen Beitrag nicht genügend Details angegeben, um anzugeben, wie dies am besten übersetzt werden kann, IObservable<T>da keine Informationen darüber vorliegen, wie Sie Es wird derzeit eine Signalisierung mit diesen Informationen gegeben.
CasperOne

@casperOne Wäre die Verwendung von Betreff Ihrer Meinung nach für einen Message Bus / Event Aggregator geeignet?
Kitsune

1
@ Kitsune Nein, ich verstehe nicht, warum sie würden. Wenn Sie an "Optimierung" denken, müssen Sie sich die Frage stellen, ob dies das Problem ist oder nicht. Haben Sie Rx als Ursache für das Problem gemessen?
CasperOne

2
Ich stimme hier mit casperOne überein, dass es eine gute Idee ist, die Bedenken aufzuteilen. Ich möchte darauf hinweisen, dass Sie die Fehlersemantik verlieren, wenn Sie das Muster Hardware to Event to Rx verwenden. Verlorene Verbindungen, Sitzungen usw. werden dem Verbraucher nicht ausgesetzt. Jetzt kann der Verbraucher nicht entscheiden, ob er es erneut versuchen, trennen, eine andere Sequenz oder etwas anderes abonnieren möchte.
Lee Campbell

0

Es ist schlecht zu verallgemeinern, dass Themen nicht gut für eine öffentliche Schnittstelle verwendet werden können. Es ist sicherlich richtig, dass ein reaktiver Programmieransatz nicht so aussehen sollte, aber es ist definitiv eine gute Option zur Verbesserung / Umgestaltung Ihres klassischen Codes.

Wenn Sie eine normale Eigenschaft mit einem öffentlichen Set-Accessor haben und über Änderungen benachrichtigen möchten, spricht nichts dagegen, sie durch ein BehaviorSubject zu ersetzen. INPC oder andere andere Ereignisse sind einfach nicht so sauber und es macht mich persönlich fertig. Zu diesem Zweck können und sollten Sie BehaviorSubjects als öffentliche Eigenschaften anstelle von normalen Eigenschaften verwenden und INPC oder andere Ereignisse aus dem Weg räumen.

Darüber hinaus macht die Betreff-Oberfläche die Benutzer Ihrer Benutzeroberfläche auf die Funktionalität Ihrer Eigenschaften aufmerksam und abonniert sie eher, als nur den Wert zu erhalten.

Es ist am besten zu verwenden, wenn andere Änderungen einer Eigenschaft abhören / abonnieren sollen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.