IObserver <T> ของ. NET มีไว้สำหรับการสมัครสมาชิกกับ IObservables หลายรายการหรือไม่


9

มีอินเทอร์เฟซIObservableและIObserverใน. NET ( ที่นี่และที่นี่ด้วย ) ที่น่าสนใจคือการใช้งาน IObserver อย่างเป็นรูปธรรมไม่ได้ถือการอ้างอิงโดยตรงกับ IObservable ไม่รู้ว่าใครเป็นสมาชิก มันสามารถเรียก unsubscriber เท่านั้น "โปรดดึงหมุดเพื่อยกเลิกการสมัคร"

แก้ไข:ดำเนิน unsubscriber IDisposableผมคิดว่าโครงการนี้ถูกจ้างมาเพื่อป้องกันไม่ให้ปัญหาผ่านพ้นไปฟัง

แม้ว่าสองสิ่งนั้นยังไม่ชัดเจนนักสำหรับฉัน

  1. ชั้น Unsubscriber ภายในให้พฤติกรรมการสมัครและลืม? ใคร (และเมื่อใด) โทรIDisposable.Dispose()ไปที่ Unsubscriber? ตัวรวบรวมขยะ (GC) ไม่ได้กำหนดไว้ล่วงหน้า
    [ข้อจำกัดความรับผิดชอบ: โดยรวมฉันใช้เวลากับ C และ C ++ มากกว่า C # แล้ว]
  2. จะเกิดอะไรขึ้นถ้าฉันต้องการสมัครเป็นผู้สังเกตการณ์ K กับ L1 ที่สังเกตได้และผู้สังเกตการณ์ได้สมัครสมาชิก L2 ที่สังเกตได้อื่นแล้ว

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

    เมื่อฉันรันโค้ดทดสอบนี้กับตัวอย่างของ MSDN ผู้สังเกตการณ์ยังคงสมัครเป็นสมาชิกของ L1 นี่จะแปลกในการพัฒนาที่แท้จริง อาจมีลู่ทาง 3 ทางในการปรับปรุงสิ่งนี้:

    • หากผู้สังเกตการณ์มีอินสแตนซ์ unsubscriber (เช่นสมัครสมาชิกแล้ว) ก็จะยกเลิกการสมัครสมาชิกอย่างเงียบ ๆ จากผู้ให้บริการดั้งเดิมก่อนสมัครเป็นสมาชิกใหม่ วิธีนี้ซ่อนความจริงที่ว่ามันไม่ได้สมัครเป็นสมาชิกกับผู้ให้บริการเดิมอีกต่อไปซึ่งอาจแปลกใจในภายหลัง
    • หากผู้สังเกตการณ์มีอินสแตนซ์ unsubscriber อยู่แสดงว่ามีข้อยกเว้น รหัสการโทรที่มีความประพฤติดีจะต้องยกเลิกการสมัครของผู้สังเกตการณ์อย่างชัดเจน
    • ผู้สังเกตการณ์สมัครรับข้อมูลจากผู้ให้บริการหลายราย นี่เป็นตัวเลือกที่น่าสนใจที่สุด แต่สิ่งนี้สามารถนำไปใช้กับ IObservable และ IObserver ได้หรือไม่? มาดูกัน. เป็นไปได้สำหรับผู้สังเกตการณ์ที่จะเก็บรายการของวัตถุผู้สมัครสมาชิก: หนึ่งรายการสำหรับแต่ละแหล่งข้อมูล น่าเสียดายที่IObserver.OnComplete()ไม่ได้ให้ข้อมูลอ้างอิงกลับไปยังผู้ให้บริการที่ส่งมา ดังนั้นการใช้งาน IObserver กับผู้ให้บริการหลายรายจะไม่สามารถกำหนดได้ว่าจะยกเลิกการสมัครใด
  3. IObserver ของ. NET มีไว้สำหรับการสมัครสมาชิกหลาย IObservables หรือไม่
    นิยามตำราเรียนของรูปแบบผู้สังเกตการณ์กำหนดให้ผู้สังเกตการณ์คนใดคนหนึ่งต้องสามารถสมัครสมาชิกกับผู้ให้บริการหลาย ๆ คนได้หรือไม่? หรือมันเป็นตัวเลือกและขึ้นอยู่กับการใช้งาน?

คำตอบ:


5

อินเทอร์เฟซสองตัวเป็นส่วนหนึ่งของส่วนขยายรีแอกทีฟ (สั้น ๆ ) คุณควรใช้ไลบรารีนั้นค่อนข้างมากทุกครั้งที่คุณต้องการใช้

อินเตอร์เฟสเป็นเทคนิคใน mscrolib ไม่ใช่ในชุดประกอบ Rx ใด ๆ ฉันคิดว่านี่คือความสะดวกในการทำงานร่วมกัน: วิธีนี้ไลบรารีเช่นTPL Dataflowสามารถให้สมาชิกที่ทำงานกับอินเทอร์เฟซเหล่านั้นได้โดยไม่ต้องอ้างอิง Rx

หากคุณใช้ Rx Subjectเป็นการดำเนินการของIObservableคุณSubscribeจะส่งคืนสิ่ง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

เพื่อล้างสิ่งเล็ก ๆ น้อย ๆ ที่ได้รับการบันทึกไว้อย่างเป็นทางการในแนวทางการออกแบบ Rxอย่างเป็นทางการและบนเว็บไซต์ของฉันIntroToRx.com :

  • คุณไม่ต้องพึ่งพา GC เพื่อล้างข้อมูลการสมัครของคุณ ครอบคลุมรายละเอียดที่นี่
  • ไม่มีUnsubscribeวิธีการ คุณสมัครเป็นสมาชิกลำดับที่สังเกตได้และจะได้รับการสมัครสมาชิก จากนั้นคุณสามารถกำจัดการสมัครสมาชิกที่ระบุว่าคุณไม่ต้องการให้มีการเรียกกลับของคุณอีกต่อไป
  • ลำดับที่สังเกตได้ไม่สามารถทำให้เสร็จสมบูรณ์ได้มากกว่าหนึ่งครั้ง (ดูหัวข้อ 4 ของแนวทางการออกแบบ Rx)
  • มีวิธีมากมายในการใช้ลำดับที่สังเกตได้หลายอย่าง นอกจากนี้ยังมีข้อมูลมากมายเกี่ยวกับเรื่องนั้นในReactivex.ioและอีกครั้งที่ IntroToRx

หากต้องการเจาะจงและตอบคำถามเดิมโดยตรงการใช้งานของคุณจะกลับไปข้างหน้า คุณไม่ได้ผลักลำดับที่สังเกตได้จำนวนมากให้เป็นผู้สังเกตการณ์คนเดียว คุณเขียนลำดับที่สังเกตได้เป็นลำดับที่สังเกตได้เดียว จากนั้นคุณสมัครสมาชิกลำดับเดียวนั้น

แทน

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

ซึ่งเป็นรหัสหลอกและจะไม่ทำงานใน. NET การใช้ Rx คุณควรทำดังต่อไปนี้

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();

ตอนนี้สิ่งนี้ไม่ตรงกับคำถามเริ่มต้น แต่ฉันไม่รู้ว่าK.Unsubscribe()ควรทำอย่างไร (ยกเลิกการสมัครทั้งหมดการสมัครครั้งสุดท้ายหรือการสมัครครั้งแรก!)


ฉันสามารถใส่อ็อบเจกต์การสมัครสมาชิกในบล็อก "กำลังใช้งาน" ได้หรือไม่?
Robert Oschler

1
ในกรณีแบบซิงโครนัสนี้คุณสามารถทำได้อย่างไรก็ตาม Rx ควรจะเป็นแบบอะซิงโครนัส ในกรณีแบบอะซิงโครนัสคุณไม่สามารถใช้usingบล็อกได้ตามปกติ ค่าใช้จ่ายสำหรับใบแจ้งยอดการสมัครสมาชิกควรเป็นศูนย์ดังนั้นคุณจะต้องใช้บล็อก, สมัคร, ออกจากบล็อกที่ใช้ (จึงยกเลิกการสมัคร) ทำให้รหัสค่อนข้างไม่มีจุดหมาย
Lee Campbell

3

คุณถูก. ตัวอย่างทำงานได้ไม่ดีสำหรับ IObservables หลายรายการ

ฉันเดาว่า OnComplete () ไม่ได้ให้การอ้างอิงกลับเพราะพวกเขาไม่ต้องการให้ IObservable ต้องเก็บมันไว้รอบ ๆ หากฉันกำลังเขียนว่าฉันอาจจะสนับสนุนการสมัครรับข้อมูลหลายรายการโดยให้สมัครรับตัวระบุเป็นพารามิเตอร์ตัวที่สองซึ่งจะถูกส่งกลับไปยังการเรียก OnComplete () ดังนั้นคุณสามารถพูดได้

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

ตามที่ปรากฏจะปรากฏว่า. NET IObserver ไม่เหมาะสำหรับผู้สังเกตการณ์หลายคน แต่ฉันคิดว่าวัตถุหลักของคุณ (ตัวอย่างเช่น LocationReporter) อาจมี

public Dictionary<String,IObserver> Observers;

และนั่นจะช่วยให้คุณสามารถสนับสนุน

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

เช่นกัน

ฉันสมมติว่า Microsoft สามารถโต้แย้งได้ว่าดังนั้นจึงไม่จำเป็นสำหรับพวกเขาที่จะสนับสนุน IObservables หลายรายการในอินเทอร์เฟซโดยตรง


ฉันยังคิดว่าการใช้งานที่สังเกตได้อาจมีรายการของผู้สังเกตการณ์ ฉันก็สังเกตเห็นIObserver.OnComplete()ว่าไม่ได้ระบุว่าใครเป็นคนโทรมา หากผู้สังเกตการณ์สมัครสมาชิกมากกว่าหนึ่งคนที่สังเกตได้ก็จะไม่รู้ว่าจะเลิกสมัครใคร เสื่อม ฉันสงสัยว่า. NET มีอินเตอร์เฟสที่ดีกว่าสำหรับรูปแบบผู้สังเกตการณ์หรือไม่
Nick Alexeev

หากคุณต้องการมีการอ้างอิงถึงบางสิ่งคุณควรใช้การอ้างอิงไม่ใช่สตริง
svick

คำตอบนี้ช่วยฉันในการแก้ไขข้อบกพร่องในชีวิตจริง ฉันถูกใช้Observable.Create()ในการสร้างที่สังเกตและผูกมัด observables Subscribe()แหล่งที่มาหลายเป็นได้โดยใช้ ฉันเผลอผ่านสิ่งที่สังเกตได้อย่างสมบูรณ์ในหนึ่งเส้นทางรหัส สิ่งนี้เสร็จสมบูรณ์ซึ่งสร้างขึ้นใหม่ที่สังเกตได้ของฉันแม้ว่าแหล่งข้อมูลอื่นจะไม่สมบูรณ์ เอายุคสมัยของฉันไปหาสิ่งที่ฉันต้องทำ - เปลี่ยนObservable.Empty()ให้Observable.Never()เป็น
Olly

0

ฉันรู้ว่านี่เป็นวิธีที่ปลายไปงานเลี้ยง แต่ ...

อินเตอร์เฟซที่ฉันObservable<T>และIObserver<T>มีไม่ได้เป็นส่วนหนึ่งของ Rx ... พวกเขากำลังประเภทหลัก ... แต่ Rx ทำให้การใช้งานที่กว้างขวางของพวกเขา

คุณมีอิสระที่จะมีผู้สังเกตการณ์ (หรือน้อย) มากเท่าที่คุณต้องการ หากคุณคาดการณ์ผู้สังเกตการณ์หลายคนเป็นความรับผิดชอบของผู้สังเกตการณ์ในการกำหนดเส้นทางการOnNext()โทรไปยังผู้สังเกตการณ์ที่เหมาะสมสำหรับแต่ละเหตุการณ์ที่สังเกต สิ่งที่สังเกตได้อาจต้องมีรายการหรือพจนานุกรมตามที่คุณแนะนำ

มีกรณีที่ดีสำหรับการอนุญาตเพียงหนึ่ง - และกรณีที่ดีสำหรับการอนุญาตจำนวนมาก ตัวอย่างเช่นในการใช้งาน CQRS / ES คุณอาจบังคับใช้ตัวจัดการคำสั่งเดียวต่อชนิดคำสั่งบนบัสคำสั่งในขณะที่คุณอาจแจ้งให้ทราบถึงการแปลงด้านการอ่านหลายประเภทสำหรับประเภทเหตุการณ์ที่กำหนดในที่จัดเก็บเหตุการณ์

ตามที่ระบุไว้ในคำตอบอื่น ๆ Unsubscribeไม่มี การกำจัดสิ่งที่คุณได้รับเมื่อคุณSubscribeทำงานสกปรกโดยทั่วไป ผู้สังเกตการณ์หรือตัวแทนดังกล่าวเป็นผู้รับผิดชอบสำหรับการถือครองลงบนโทเค็นจนกว่าจะไม่ต้องการที่จะรับการแจ้งเตือน (คำถามที่ 1)

ดังนั้นในตัวอย่างของคุณ:

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

... มันจะเป็นเช่น:

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 );
}

... ที่ K จะได้ยิน 1003 และ 1004 แต่ไม่ใช่ 1005

สำหรับฉันมันยังคงดูตลกเพราะในนามการสมัครสมาชิกเป็นสิ่งที่ยาวนาน ... มักจะเป็นช่วงระยะเวลาของโปรแกรม พวกเขาไม่ได้แตกต่างกันในแง่นี้กับเหตุการณ์. net ปกติ

ในตัวอย่างจำนวนมากที่ฉันได้เห็นDisposeโทเค็นทำงานเพื่อลบผู้สังเกตการณ์ออกจากรายชื่อผู้สังเกตการณ์ที่สังเกตได้ ฉันชอบที่โทเค็นไม่ได้มีความรู้รอบตัวมากนัก ... และดังนั้นฉันจึงได้ทั่วไปโทเค็นการสมัครสมาชิกของฉันเพื่อโทรแลมบ์ดาแบบส่งผ่าน (ด้วยข้อมูลที่ระบุตัวตนที่บันทึกในเวลาสมัคร):

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

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

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

... และสิ่งที่สังเกตได้สามารถติดตั้งพฤติกรรมการยกเลิกการเป็นสมาชิกในระหว่างการสมัคร:

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

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

ถ้าผู้สังเกตการณ์ของคุณจะจับเหตุการณ์จาก observables หลายท่านอาจจะต้องการให้แน่ใจว่ามีชนิดของข้อมูลที่มีความสัมพันธ์ในเหตุการณ์ที่เกิดขึ้นบางตัวเอง ... senderเหมือนเหตุการณ์สุทธิทำอย่างไรกับ มันขึ้นอยู่กับคุณไม่ว่าจะสำคัญหรือไม่ก็ตาม มันไม่ได้อบในขณะที่คุณให้เหตุผลอย่างถูกต้อง (คำถาม 3)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.