Istnieją interfejsy IObservable i IObserver w .NET (także tu i tutaj ). Co ciekawe, konkretne wdrożenie IObserver nie zawiera bezpośredniego odniesienia do IObservable. Nie wie, kto jest subskrybentem. Może tylko wywołać subskrybenta. „Wyciągnij pinezkę, aby anulować subskrypcję”.
edycja: Subskrybent implementuje IDisposable
. Myślę, że ten schemat został zastosowany, aby zapobiec problemowi z wygasłym słuchaczem .
Jednak dwie rzeczy nie są do końca jasne.
- Czy wewnętrzna klasa Unsubscriber zapewnia zachowanie „zapisz się i zapomnij”? Kto (i kiedy dokładnie) wzywa
IDisposable.Dispose()
abonenta? Garbage collector (GC) nie jest deterministyczny.
[Uwaga: ogólnie, spędziłem więcej czasu z C i C ++ niż z C #.] Co powinno się stać, jeśli chcę subskrybować obserwatora K do obserwowalnego L1, a obserwator jest już subskrybowany do innego obserwowalnego L2?
K.Subscribe(L1); K.Subscribe(L2); K.Unsubscribe(); L1.PublishObservation(1003); L2.PublishObservation(1004);
Kiedy uruchomiłem ten kod testowy na przykładzie MSDN, obserwator pozostał subskrybowany do L1. Byłoby to szczególne w prawdziwym rozwoju. Potencjalnie istnieją 3 sposoby na poprawę tego:
- Jeśli obserwator ma już instancję subskrybenta (tj. Jest już subskrybowany), wówczas cicho wypisuje się z pierwotnego dostawcy przed subskrybowaniem nowego. Takie podejście ukrywa fakt, że nie jest już subskrybentem pierwotnego dostawcy, co może później stać się niespodzianką.
- Jeśli obserwator ma już instancję subskrybenta, zgłasza wyjątek. Dobrze zachowujący się kod wywołujący musi jawnie wypisać się z listy obserwatorów.
- Obserwator subskrybuje wielu dostawców. Jest to najbardziej intrygująca opcja, ale czy można to zaimplementować za pomocą IObservable i IObserver? Zobaczmy. Obserwator może przechowywać listę obiektów niesubskrybujących: po jednym dla każdego źródła. Niestety
IObserver.OnComplete()
nie udostępnia referencji dostawcy, który ją wysłał. Tak więc implementacja IObserver z wieloma dostawcami nie byłaby w stanie określić, z którego z nich zrezygnować.
Czy IObserver .NET był przeznaczony do subskrybowania wielu IObservables?
Czy podręcznikowa definicja wzorca obserwatora wymaga, aby jeden obserwator mógł subskrybować wielu dostawców? Czy jest to opcjonalne i zależy od implementacji?
źródło
using
bloku. Koszt wyciągu z subskrypcji powinien być praktycznie zerowy, więc wyzeruj blok używający, subskrybuj, pozostaw blok blokujący (a tym samym zrezygnuj z subskrypcji), czyniąc kod raczej bezcelowymMasz rację. Przykład działa źle dla wielu IObservables.
Myślę, że OnComplete () nie zapewnia referencji, ponieważ nie chcą, aby IObservable musiał ją utrzymywać. Gdybym pisał, że prawdopodobnie wspierałbym wiele subskrypcji, każąc Subskrybować wziąć identyfikator jako drugi parametr, który jest przekazywany z powrotem do wywołania OnComplete (). Więc możesz powiedzieć
W tej chwili wygląda na to, że .NET IObserver nie jest odpowiedni dla wielu obserwatorów. Ale przypuszczam, że mógłby to być twój główny obiekt (w tym przykładzie LocationReporter)
i to umożliwi ci wsparcie
także.
Podejrzewam, że Microsoft mógłby argumentować, że w związku z tym nie ma potrzeby bezpośredniego wspierania wielu IObservables w interfejsach.
źródło
IObserver.OnComplete()
nie identyfikuje, kto pochodzi z połączenia. Jeśli obserwator jest subskrybentem więcej niż jednego obserwowalnego, nie wie, od kogo się wypisać. Rozczarowujący. Zastanawiam się, czy .NET ma lepszy interfejs dla wzorca obserwatora?Observable.Create()
do zbudowania obserwowalnego i używając do tego szeregu obserwowalnych źródełSubscribe()
. Nieumyślnie przekazałem ukończony obserwowalny w jednej ścieżce kodu. To uzupełniło moje nowo utworzone obserwowalne, mimo że inne źródła nie były kompletne. Zajęło mi wieki, aby się zorientować, co muszę zrobić - przełącznikObservable.Empty()
dlaObservable.Never()
.Wiem, że to jest sposób późno do partii, ale ...
Interfejsy I
Observable<T>
i nieIObserver<T>
są częścią Rx ... są to typy podstawowe ... ale Rx szeroko z nich korzysta.Możesz mieć dowolną liczbę (lub tak niewielu) obserwatorów. Jeśli spodziewasz się wielu obserwatorów, obowiązkiem obserwatora jest kierowanie
OnNext()
połączeń do odpowiednich obserwatorów dla każdego obserwowanego zdarzenia. Obserwowalny może wymagać listy lub słownika, jak sugerujesz.Są dobre przypadki na dopuszczenie tylko jednego - i dobre przypadki na dopuszczenie wielu. Na przykład w implementacji CQRS / ES, można wymusić jeden obsługi poleceń za poleceń typu w autobusie poleceń, a może powiadomić kilka transformacji odczytu ubocznych dla danego zdarzenia typu w składnicy zdarzeń.
Jak stwierdzono w innych odpowiedziach, nie ma
Unsubscribe
. Pozbywanie się rzeczy, które dostajesz, gdySubscribe
generalnie wykonujesz brudną robotę. Obserwator lub jego agent jest odpowiedzialny za trzymanie tokena, dopóki nie będzie już chciał otrzymywać dalszych powiadomień . (pytanie 1)W twoim przykładzie:
... byłoby bardziej jak:
... gdzie K usłyszy 1003 i 1004, ale nie 1005.
Dla mnie nadal wygląda to zabawnie, ponieważ nominalnie subskrypcje to rzeczy długowieczne ... często na czas trwania programu. Nie różnią się pod tym względem zwykłymi zdarzeniami .Net.
W wielu przykładach, które widziałem,
Dispose
token działa usuwając obserwatora z listy obserwatorów. Wolę, aby token nie zawierał tyle wiedzy wokół ... i dlatego uogólniłem tokeny subskrypcji, aby wywoływały przekazaną lambda (z informacjami identyfikującymi przechwyconymi w czasie subskrypcji:... a obserwowalne mogą zainstalować zachowanie anulowania subskrypcji podczas subskrypcji:
Jeśli Twój obserwator przechwytuje zdarzenia z wielu obserwowalnych obiektów, możesz chcieć upewnić się, że w samych zdarzeniach znajduje się jakiś rodzaj korelacji ... tak jak w przypadku zdarzeń .Net
sender
. To od Ciebie zależy, czy to ma znaczenie, czy nie. Nie jest upieczony, jak prawidłowo uzasadniłeś. (pytanie 3)źródło