.NET'in IObserver <T> birden çok IObservable'a abone olmak için mi tasarlandı?


9

Orada IObservable ve IObserver .NET (aynı zamanda arayüzleri burada ve burada ). İlginç bir şekilde, IObserver'ın somut uygulaması IObservable'a doğrudan bir referans içermemektedir. Kime abone olduğunu bilmiyor. Sadece aboneliği iptal edebilir. "Aboneliği iptal etmek için lütfen raptiyeyi çekin."

edit: Unsubscriber uygular IDisposable. Bence bu şema, dinleyen din problemini önlemek için kullanıldı .

Yine de iki şey benim için tamamen açık değil.

  1. İç Unsubscriber sınıfı abone ol ve unut davranışını sağlıyor mu? IDisposable.Dispose()Aboneliği kim (ve tam olarak ne zaman) çağırıyor ? Çöp toplayıcı (GC) deterministik değildir.
    [Feragatname: Genel olarak, C ve C ++ ile C # 'den daha fazla zaman geçirdim.]
  2. Bir gözlemci K'yi gözlemlenebilir bir L1'e abone olmak istersem ve gözlemci başka bir gözlemlenebilir L2'ye zaten abone olursa ne olur?

    K.Subscribe(L1);
    K.Subscribe(L2);
    K.Unsubscribe();
    L1.PublishObservation(1003);
    L2.PublishObservation(1004);
    

    Bu test kodunu MSDN örneğine karşı çalıştırdığımda, gözlemci L1'e abone kaldı. Bu gerçek gelişimde kendine özgüdür. Potansiyel olarak, bunu iyileştirmek için 3 yol vardır:

    • Gözlemcinin zaten bir abonelikten çıkma örneği varsa (yani zaten abone olmuşsa), yenisine abone olmadan önce orijinal sağlayıcıdan sessizce aboneliği iptal eder. Bu yaklaşım, artık orijinal sağlayıcıya abone olmadığı gerçeğini gizler, bu da daha sonra sürpriz olabilir.
    • Gözlemcinin zaten abonelikten çıkmış bir örneği varsa, bir istisna atar. İyi davranılmış bir çağrı kodu, gözlemciyi açıkça iptal etmelidir.
    • Gözlemci birden çok sağlayıcıya abone olur. Bu en ilginç seçenektir, ancak bu IObservable ve IObserver ile uygulanabilir mi? Bakalım. Gözlemcinin, abone olmayan nesnelerin bir listesini tutması mümkündür: her kaynak için bir tane. Maalesef, IObserver.OnComplete()gönderen sağlayıcıya bir referans sağlamaz. Bu nedenle, birden çok sağlayıcı ile IObserver uygulaması hangisinin aboneliğini iptal edeceğini belirleyemez.
  3. .NET'in IObserver'ı birden çok IObservable'a abone olmayı amaçladı mı?
    Gözlemci modelinin ders kitabı tanımı, bir gözlemcinin birden çok sağlayıcıya abone olmasını gerektiriyor mu? Yoksa isteğe bağlı ve uygulamaya bağlı mı?

Yanıtlar:


5

İki arayüz aslında Reaktif Uzantıların bir parçasıdır (kısaca Rx), bu kütüphaneyi kullanmak istediğinizde hemen hemen kullanmalısınız.

Arabirimler teknik olarak mscrolib'dedir, Rx derlemelerinin hiçbirinde değildir. Bunun birlikte çalışabilirliği kolaylaştırmak olduğunu düşünüyorum: bu şekilde, TPL Dataflow gibi kütüphaneler aslında Rx'e başvurmadan bu arayüzlerle çalışan üyeler sağlayabilir .

SubjectUygulamanız olarak Rx'leri kullanırsanız IObservable, abonelikten çıkma için kullanılabilecek Subscribebir döndürür IDisposable:

var observable = new Subject<int>();

var unsubscriber =
    observable.Subscribe(Observer.Create<int>(i => Console.WriteLine("1: {0}", i)));
observable.Subscribe(Observer.Create<int>(i => Console.WriteLine("2: {0}", i)));

unsubscriber.Dispose();

observable.OnNext(1003);
observable.OnNext(1004);

5

Sadece resmi Rx Tasarım Yönergeleri'nde ve web sitemde IntroToRx.com uzunluğunda belgelenmiş birkaç şeyi temizlemek için :

  • Aboneliklerinizi temizlemek için GC'ye güvenmezsiniz. Burada ayrıntılı olarak ele alınmıştır
  • Bir Unsubscribeyöntem yok . Gözlenebilir bir sıraya abone olursunuz ve size bir abonelik verilir . Daha sonra artık geri aramalarınızı çağırmak istemediğinizi belirten aboneliği atabilirsiniz.
  • Gözlenebilir bir dizi bir kereden fazla tamamlanamaz (bkz. Rx Tasarım Yönergeleri, bölüm 4).
  • Birden fazla gözlemlenebilir dizi tüketmenin birçok yolu vardır. Reactivex.io'da ve tekrar IntroToRx'te bununla ilgili zengin bir bilgi var .

Spesifik olmak ve orijinal soruyu doğrudan cevaplamak için kullanımınız öne çıkar. Gözlenebilir birçok diziyi tek bir gözlemciye itmezsiniz. Gözlenebilir dizileri tek bir gözlemlenebilir diziye dönüştürürsünüz. Daha sonra bu tek diziye abone olursunuz.

Onun yerine

K.Subscribe(L1);
K.Subscribe(L2);
K.Unsubscribe();
L1.PublishObservation(1003);
L2.PublishObservation(1004);

Hangi sadece sözde kod ve Rx .NET uygulamasında işe yaramaz, aşağıdakileri yapmalısınız:

var source1 = new Subject<int>(); //was L1
var source2 = new Subject<int>(); //was L2

var subscription = source1
    .Merge(source2)
    .Subscribe(value=>Console.WriteLine("OnNext({0})", value));


source1.OnNext(1003);
source2.OnNext(1004);

subscription.Dispose();

Şimdi bu ilk soruya tam olarak uymuyor, ama ne K.Unsubscribe()yapması gerektiğini bilmiyorum (herkesten çık, son veya ilk abonelik ?!)


Abonelik nesnesini bir "kullanma" bloğunda sarabilir miyim?
Robert Oschler

1
Bu eşzamanlı durumda yapabilirsiniz, ancak Rx'in eşzamansız olması gerekir. Eşzamansız durumda, normalde usingbloğu kullanamazsınız . Bir abonelik ifadesinin maliyeti neredeyse sıfır olmalıdır, bu nedenle, kullanım bloğunu netleştirir, abone olur, kullanım bloğunu terk edersiniz (böylece abonelikten ayrılır) kodu oldukça anlamsız hale getirirsiniz
Lee Campbell

3

Haklısın. Örnek, birden çok IObservable için kötü çalışıyor.

OnComplete () bir referans geri vermiyor sanırım çünkü onlar IObservable onu tutmak zorunda istemiyorum. Eğer muhtemelen OnComplete () çağrısına geri gönderilen abone ikinci bir parametre olarak bir tanımlayıcı almak alarak birden fazla abonelik destek olacağını yazıyordu. Yani söyleyebilirsin

K.Subscribe(L1,"L1")
K.Subscribe(L2,"L2")
K.Unsubscribe("L1")

Göründüğü gibi, .NET IObserver'ın birden fazla gözlemci için uygun olmadığı anlaşılıyor. Ama sanırım ana nesneniz (örnekteki LocationReporter)

public Dictionary<String,IObserver> Observers;

ve bu size

K.Subscribe(L1,"L1")
K.Subscribe(L2,"L2")
K.Unsubscribe("L1")

de.

Microsoft'un bu nedenle arabirimlerde birden çok IObservable'ı doğrudan desteklemelerine gerek olmadığını iddia edebilirim.


Ayrıca gözlemlenebilir uygulamanın gözlemcilerin bir listesine sahip olabileceğini düşünüyordum. Ben de IObserver.OnComplete()aramanın kimden geldiğini tanımlamadığını fark ettim . Gözlemci birden fazla gözlemlenebilirliğe abone olursa, kimden aboneliği iptal edeceğini bilmez. Anticlimactic. Merak ediyorum, .NET'in gözlemci modeli için daha iyi bir arayüzü var mı?
Nick Alexeev

Bir şeye başvuru yapmak istiyorsanız, aslında bir dize değil bir referans kullanmalısınız.
svick

Bu cevap bana gerçek hayattaki bir hata ile yardımcı oldu. Observable.Create()Bir gözlemlenebilir inşa etmek için kullanıyordum ve birkaç kaynak gözlemlenebilir kullanarak zincirleme yapıyordum Subscribe(). Yanlışlıkla tamamlanmış gözlemlenebilir bir kod yolunda geçti. Bu, diğer kaynaklar tam olmasa da yeni oluşturulan gözlemlenebilirliğimi tamamladı. Yapmam gereken ne bana çalışmalarına yaş aldı - anahtarı Observable.Empty()için Observable.Never().
Olly

0

Bunun olduğunu biliyorum yolu partisine geç, ama ...

Arayüzleri ben Observable<T>ve IObserver<T>vardır değil Rx ... bunlar birer çekirdek türlerinin parçası ... ama Rx bunlardan yaygın olarak kullanmaktadır.

İstediğiniz sayıda (veya az) gözlemciye sahip olmakta özgürsünüz. Birden fazla gözlemci öngörürseniz, OnNext()her gözlemlenen olay için çağrıları uygun gözlemcilere yönlendirmek gözlemlenenin sorumluluğundadır . Gözlenebilir, önerdiğiniz gibi bir listeye veya sözlüğe ihtiyaç duyabilir.

Sadece bir tanesine izin vermek için iyi durumlar ve birçokına izin vermek için iyi durumlar vardır. Örneğin, bir CQRS / ES uygulamasında, bir komut veriyolunda komut türü başına tek bir komut işleyici uygulayabilirsiniz , ancak olay deposundaki belirli bir olay türü için birden çok okuma tarafı dönüşümü bildirebilirsiniz .

Diğer cevaplarda belirtildiği gibi, hayır Unsubscribe. SubscribeGenel olarak kirli işi yaptığınızda verdiğiniz şeyi elden çıkarmak . Gözlemci veya bir ajanı, artık başka bildirim almak istemedikçe belirteci tutmaktan sorumludur . (soru 1)

Yani, örneğinizde:

K.Subscribe(L1);
K.Subscribe(L2);
K.Unsubscribe();
L1.PublishObservation(1003);
L2.PublishObservation(1004);

... daha çok şöyle olurdu:

using ( var l1Token = K.Subscribe( L1 ) )
{
  using ( var l2Token = K.Subscribe( L2 );
  {
    L1.PublishObservation( 1003 );
    L2.PublishObservation( 1004 );
  } //--> effectively unsubscribing to L2 here

  L2.PublishObservation( 1005 );
}

... burada K 1003 ve 1004'ü duyacaktı, ama 1005'i duyamayacaktı.

Bana göre, bu hala komik görünüyor çünkü nominal olarak, abonelikler uzun ömürlü şeyler ... genellikle program süresince. Normal .Net olaylarına bu açıdan benzemezler.

Gördüğüm birçok örnekte Dispose, jetonun işareti, gözlemciyi gözlemlenenin gözlemciler listesinden çıkarmak için çalışıyor. Simgenin bu kadar fazla bilgi taşımamasını tercih ediyorum ... ve bu yüzden abonelik jetonlarımı sadece bir geçirilen lambda'yı çağırmak için genelleştirdim (abone zamanında yakalanan bilgileri tanımlayarak:

public class SubscriptionToken<T>: IDisposable
{
  private readonly Action unsubscribe;

  private SubscriptionToken( ) { }
  public SubscriptionToken( Action unsubscribe )
  {
    this.unsubscribe = unsubscribe;
  }

  public void Dispose( )
  {
    unsubscribe( );
  }
}

... ve gözlemlenen, abonelik sırasında abonelikten çıkma davranışını yükleyebilir:

IDisposable Subscribe<T>( IObserver<T> observer )
{
  var subscriberId = Guid.NewGuid( );
  subscribers.Add( subscriberId, observer );

  return new SubscriptionToken<T>
  (
    ( ) =>
    subscribers.Remove( subscriberId );
  );
}

Eğer gözlemciniz birden fazla gözlemlenebilir olaydan olay yakalarsa, olayların kendisinde bir tür korelasyon bilgisi olduğundan emin olmak isteyebilirsiniz. .Net olayları sender. Bunun önemli olup olmadığı size bağlıdır. Doğru mantık yürüttüğünüz gibi pişmemiş. (Soru 3)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.