Có phải IObserver của .NET <T> dự định đăng ký nhiều IObservables không?


9

Có giao diện IObservableIObserver trong .NET (cũng ở đâyđây ). Thật thú vị, việc triển khai cụ thể của IObserver không có tham chiếu trực tiếp đến IObservable. Nó không biết ai đã đăng ký. Nó chỉ có thể gọi người đăng ký. "Hãy kéo pin để hủy đăng ký."

chỉnh sửa: Người đăng ký thực hiện IDisposable. Tôi nghĩ rằng, kế hoạch này đã được sử dụng để ngăn chặn vấn đề người nghe mất hiệu lực .

Hai điều không hoàn toàn rõ ràng với tôi, mặc dù.

  1. Có phải lớp Unsubscacker bên trong cung cấp hành vi đăng ký và quên? Ai (và khi chính xác) gọi IDisposable.Dispose()cho Unsubscacker? Công cụ thu gom rác (GC) không mang tính quyết định.
    [Tuyên bố miễn trừ trách nhiệm: nói chung, tôi đã dành nhiều thời gian với C và C ++ hơn so với C #.]
  2. Điều gì sẽ xảy ra nếu tôi muốn đăng ký một người quan sát K đến một L1 có thể quan sát được và người quan sát đã đăng ký một số L2 có thể quan sát khác?

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

    Khi tôi chạy mã kiểm tra này với ví dụ của MSDN, người quan sát vẫn đăng ký L1. Đây sẽ là đặc thù trong phát triển thực sự. Có khả năng, có 3 con đường để cải thiện điều này:

    • Nếu người quan sát đã có một cá thể người đăng ký (nghĩa là nó đã được đăng ký), thì nó lặng lẽ hủy đăng ký từ nhà cung cấp ban đầu trước khi đăng ký một cái mới. Cách tiếp cận này che giấu thực tế rằng nó không còn đăng ký vào nhà cung cấp ban đầu, điều này có thể trở thành một bất ngờ sau này.
    • Nếu người quan sát đã có một cá thể người đăng ký, thì đó là một ngoại lệ. Một mã gọi hành xử tốt phải hủy đăng ký người quan sát một cách rõ ràng.
    • Người quan sát đăng ký vào nhiều nhà cung cấp. Đây là tùy chọn hấp dẫn nhất, nhưng điều này có thể được thực hiện với IObservable và IObserver không? Hãy xem nào. Người quan sát có thể giữ một danh sách các đối tượng người đăng ký: một cho mỗi nguồn. Thật không may, IObserver.OnComplete()không cung cấp một tài liệu tham khảo lại cho nhà cung cấp đã gửi nó. Vì vậy, việc triển khai IObserver với nhiều nhà cung cấp sẽ không thể xác định được ai sẽ hủy đăng ký.
  3. Có phải IObserver của .NET dự định đăng ký nhiều IObservables không?
    Có phải định nghĩa sách giáo khoa về mẫu người quan sát yêu cầu một người quan sát phải có thể đăng ký vào nhiều nhà cung cấp không? Hoặc là tùy chọn và phụ thuộc vào việc thực hiện?

Câu trả lời:


5

Hai giao diện thực sự là một phần của Phần mở rộng phản ứng (viết tắt là Rx), bạn nên sử dụng thư viện đó khá nhiều bất cứ khi nào bạn muốn sử dụng chúng.

Các giao diện về mặt kỹ thuật là mscrolib, không phải trong bất kỳ hội đồng Rx nào. Tôi nghĩ rằng điều này là để giảm bớt khả năng tương tác: theo cách này, các thư viện như TPL Dataflow có thể cung cấp cho các thành viên làm việc với các giao diện đó mà không cần tham khảo Rx.

Nếu bạn sử dụng Rx Subjectnhư là triển khai của bạn IObservable, Subscribesẽ trả về một IDisposablecái có thể được sử dụng để hủy đăng ký:

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

Chỉ cần làm rõ một vài điều được ghi lại rõ ràng trong Nguyên tắc thiết kế Rx chính thức và theo chiều dài trên trang web của tôi IntroToRx.com :

  • Bạn không dựa vào GC để dọn dẹp đăng ký của bạn. Bao phủ chi tiết ở đây
  • Không có Unsubscribephương pháp. Bạn đăng ký một chuỗi có thể quan sát và được cung cấp một thuê bao . Sau đó, bạn có thể loại bỏ đăng ký đó chỉ ra rằng bạn không còn muốn gọi lại.
  • Một chuỗi có thể quan sát không thể được hoàn thành nhiều hơn một lần (xem phần 4 của Nguyên tắc thiết kế Rx).
  • Có nhiều cách để tiêu thụ nhiều chuỗi có thể quan sát được. Ngoài ra còn có rất nhiều thông tin liên quan đến điều đó trên Reactivex.io và một lần nữa tại IntroToRx.

Để được cụ thể và trả lời trực tiếp câu hỏi ban đầu, việc sử dụng của bạn trở lại phía trước. Bạn không đẩy nhiều trình tự quan sát vào một người quan sát. Bạn kết hợp các chuỗi có thể quan sát thành một chuỗi có thể quan sát được. Sau đó, bạn đăng ký vào chuỗi duy nhất đó.

Thay vì

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

Đó chỉ là mã giả và sẽ không hoạt động trong triển khai .NET của Rx, bạn nên làm như sau:

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

Bây giờ điều này không chính xác phù hợp với câu hỏi ban đầu, nhưng tôi không biết K.Unsubscribe()phải làm gì (hủy đăng ký từ tất cả, đăng ký cuối cùng hoặc đăng ký đầu tiên?!)


Tôi có thể chỉ đơn giản là bao bọc đối tượng đăng ký trong một khối "sử dụng" không?
Robert Oschler

1
Trong trường hợp đồng bộ này, bạn có thể, tuy nhiên Rx được coi là không đồng bộ. Trong trường hợp không đồng bộ, thông thường bạn không thể sử dụng usingkhối. Chi phí cho một tuyên bố đăng ký sẽ gần như bằng 0, vì vậy bạn sẽ kiểm tra khối sử dụng, đăng ký, để lại khối sử dụng (do đó không đăng ký) làm cho mã trở nên vô nghĩa
Lee Campbell

3

Bạn đúng. Ví dụ này hoạt động kém cho nhiều IObservables.

Tôi đoán OnComplete () không cung cấp tài liệu tham khảo vì họ không muốn IObservable phải giữ nó xung quanh. Nếu tôi viết rằng tôi có thể sẽ hỗ trợ nhiều đăng ký bằng cách Đăng ký lấy số nhận dạng làm tham số thứ hai, được chuyển trở lại cuộc gọi OnComplete (). Vì vậy, bạn có thể nói

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

Như hiện tại, nó xuất hiện .NET IObserver không phù hợp với nhiều người quan sát. Nhưng tôi cho rằng đối tượng chính của bạn (ví dụ LocationReporter) có thể có

public Dictionary<String,IObserver> Observers;

và điều đó sẽ cho phép bạn hỗ trợ

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

cũng.

Tôi cho rằng Microsoft có thể lập luận rằng do đó không cần họ hỗ trợ trực tiếp nhiều IObservables trong các giao diện.


Tôi cũng đã nghĩ rằng việc thực hiện có thể quan sát được có thể có một danh sách các nhà quan sát. Tôi cũng đã nhận thấy rằng IObserver.OnComplete()không xác định được cuộc gọi đến từ ai. Nếu người quan sát được đăng ký nhiều hơn một người có thể quan sát, thì nó không biết ai sẽ hủy đăng ký. Chống sốt rét. Tôi tự hỏi, .NET có giao diện tốt hơn cho mẫu quan sát viên không?
Nick Alexeev

Nếu bạn muốn có một tham chiếu đến một cái gì đó, bạn thực sự nên sử dụng một tài liệu tham khảo, không phải là một chuỗi.
Svick

Câu trả lời này đã giúp tôi với một lỗi thực tế. Tôi đã sử dụng Observable.Create()để xây dựng một thiết bị quan sát được và sử dụng nhiều thiết bị quan sát nguồn vào đó Subscribe(). Tôi đã vô tình thông qua một quan sát hoàn thành trong một đường dẫn mã. Điều này đã hoàn thành quan sát mới được tạo của tôi, mặc dù các nguồn khác không hoàn thành. Đưa tôi lứa tuổi để rèn luyện sức khỏe những gì tôi cần phải làm - chuyển đổi Observable.Empty()cho Observable.Never().
Olly

0

Tôi biết đây là cách muộn để đảng, nhưng ...

Các giao diện tôi Observable<T>IObserver<T>không một phần của Rx ... họ loại lõi đang ... nhưng Rx làm cho sử dụng rộng rãi trong số họ.

Bạn có thể tự do có nhiều (hoặc ít) người quan sát tùy thích. Nếu bạn dự đoán nhiều người quan sát, thì trách nhiệm có thể quan sát được là định tuyến OnNext()các cuộc gọi đến người quan sát phù hợp cho từng sự kiện được quan sát. Có thể quan sát có thể cần một danh sách hoặc một từ điển như bạn đề xuất.

Có những trường hợp tốt khi chỉ cho phép một - và những trường hợp tốt cho phép nhiều người. Ví dụ: trong triển khai CQRS / ES, bạn có thể thực thi một trình xử lý lệnh duy nhất cho mỗi loại lệnh trên một bus lệnh, trong khi bạn có thể thông báo cho một số biến đổi phía đọc cho một loại sự kiện nhất định trong kho sự kiện.

Như đã nêu trong các câu trả lời khác, không có Unsubscribe. Vứt bỏ những thứ bạn đưa ra khi bạn Subscribethường làm công việc bẩn thỉu. Người quan sát, hoặc một tác nhân của nó, chịu trách nhiệm giữ mã thông báo cho đến khi nó không còn muốn nhận thêm thông báo nữa . (câu 1)

Vì vậy, trong ví dụ của bạn:

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

... nó sẽ giống như:

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

... Trong đó K sẽ nghe 1003 và 1004 chứ không phải 1005.

Đối với tôi, điều này vẫn có vẻ buồn cười bởi vì trên danh nghĩa, đăng ký là những thứ tồn tại lâu dài ... thường trong suốt thời gian của chương trình. Chúng không giống nhau về các sự kiện .Net thông thường.

Trong rất nhiều ví dụ tôi đã thấy, Disposemã thông báo hoạt động để loại bỏ người quan sát khỏi danh sách người quan sát có thể quan sát được. Tôi thích rằng mã thông báo không mang nhiều kiến ​​thức xung quanh ... và vì vậy tôi đã khái quát mã thông báo đăng ký của mình để chỉ gọi lambda đã qua (với thông tin nhận dạng được ghi lại tại thời điểm đăng ký:

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

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

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

... và có thể quan sát có thể cài đặt hành vi hủy đăng ký trong khi đăng ký:

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

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

Nếu người quan sát của bạn đang nắm bắt các sự kiện từ nhiều đài quan sát, bạn có thể muốn đảm bảo rằng có một số loại thông tin tương quan trong chính các sự kiện ... giống như các sự kiện .Net làm với sender. Điều đó tùy thuộc vào bạn cho dù điều đó có quan trọng hay không. Nó không được nướng trong, như bạn đã suy luận chính xác. (câu hỏi 3)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.