.NET의 IObserver <T>가 여러 IObservable을 구독하기위한 것입니까?


9

.NET 에는 IObservableIObserver 인터페이스가 있습니다 ( 여기여기 ). 흥미롭게도 IObserver의 구체적인 구현은 IObservable에 대한 직접적인 참조를 보유하지 않습니다. 누가 구독했는지 알 수 없습니다. 구독자 만 호출 할 수 있습니다. "핀을 당겨서 탈퇴하십시오."

편집 : 탈퇴자는을 구현합니다 IDisposable. 나는이 체계가 리스너 문제 를 방지하기 위해 사용되었다고 생각한다 .

그래도 두 가지가 완전히 명확하지 않습니다.

  1. 내부 Unsubscriber 클래스는 구독 및 잊어 버림 동작을 제공합니까? 구독자를 누가 (그리고 언제 정확하게) 호출 IDisposable.Dispose()합니까? 가비지 콜렉터 (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 가지 방법이 있습니다.

    • 옵저버에 구독자 인스턴스가 이미있는 경우 (즉, 이미 구독 한 경우) 새 제공자를 구독하기 전에 원래 제공자로부터 조용히 구독을 취소합니다. 이 접근 방식은 더 이상 원래 공급자를 구독하지 않아 나중에 놀랍게 될 수 있다는 사실을 숨 깁니다.
    • 옵저버에 구독자 인스턴스가 이미 있으면 예외가 발생합니다. 올바르게 작동하는 호출 코드는 관찰자를 명시 적으로 구독 취소해야합니다.
    • 관찰자는 여러 공급자를 구독합니다. 이것이 가장 흥미로운 옵션이지만 IObservable 및 IObserver로 구현할 수 있습니까? 보자 관찰자는 구독자 오브젝트 목록을 각 소스마다 하나씩 유지할 수 있습니다. 불행히도, IObserver.OnComplete()그것을 보낸 제공자에게 참조를 제공하지 않습니다. 따라서 여러 공급자를 사용한 IObserver 구현은 어느 공급자를 구독 취소할지 결정할 수 없습니다.
  3. .NET의 IObserver는 여러 IObservable을 구독하기위한 것입니까?
    옵저버 패턴의 교재 정의는 한 옵저버가 여러 제공자를 구독 할 수 있어야합니까? 아니면 선택적이고 구현에 따라 다릅니 까?

답변:


5

두 인터페이스는 실제로 Reactive Extensions (Rx)의 일부 이므로 사용하려는 경우 언제든지 해당 라이브러리를 사용해야합니다.

인터페이스는 기술적으로 Rx 어셈블리가 아닌 mscrolib에 있습니다. 나는 이것이 상호 운용성을 용이하게하는 것이라고 생각한다. 이런 식으로, TPL Dataflow 와 같은 라이브러리는 실제로 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. 관찰 가능한 시퀀스에 가입 하면 가입 이 제공 됩니다. 그런 다음 더 이상 콜백을 호출하지 않으려는 구독을 폐기 할 수 있습니다.
  • 관찰 가능한 시퀀스는 두 번 이상 완료 될 수 없습니다 (Rx 디자인 지침의 섹션 4 참조).
  • 관찰 가능한 여러 시퀀스를 소비하는 방법은 여러 가지가 있습니다. Reactivex.io 와 IntroToRx 에 관한 정보도 풍부 합니다.

구체적으로 설명하고 원래 질문에 직접 대답하기 위해 사용량이 앞뒤로 돌아옵니다. 관찰 가능한 많은 시퀀스를 단일 관찰자에게 푸시하지 않습니다. 관찰 가능 시퀀스를 단일 관찰 가능 시퀀스로 구성합니다. 그런 다음 해당 단일 시퀀스를 구독합니다.

대신에

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

의사 코드 일 뿐이며 Rx의 .NET 구현에서 작동하지 않는 것은 다음과 같습니다.

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()을 해야할지 모르겠습니다 (마지막 구독 또는 첫 구독을 모두 구독 취소 하시겠습니까?!).


구독 객체를 "using"블록에 간단히 넣을 수 있습니까?
Robert Oschler

1
이 동기식 경우 Rx는 비동기식이어야합니다. 비동기 경우에는 일반적으로 using블록을 사용할 수 없습니다 . 서브 스크립 션 명세서의 비용은 사실상 제로가되어야합니다. 따라서 using 블록을 더 사용하고, subscribe하고, using 블록을 남겨두고 (따라서 구독 취소) 코드를 무의미하게 만듭니다
Lee Campbell

3

네가 옳아. 이 예제는 여러 IObservable에 적합하지 않습니다.

OnComplete ()은 IObservable이 그것을 유지하기를 원하지 않기 때문에 참조를 다시 제공하지 않는다고 생각합니다. 내가 쓰고 있다면 Subscribe가 식별자를 두 번째 매개 변수로 가져 와서 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가 인터페이스에서 여러 IObservable을 직접 지원할 필요가 없다고 주장 할 수 있다고 생각합니다.


또한 관찰 가능한 구현에 관찰자 목록이있을 수 있다고 생각했습니다. 나도 IObserver.OnComplete()전화가 온 사람을 식별하지 못하는 것으로 나타났습니다 . 옵저버가 둘 이상의 옵저버 블을 구독하면 누가 구독을 취소해야하는지 알 수 없습니다. 촉각. .NET에 옵저버 패턴에 대한 더 나은 인터페이스가 있습니까?
Nick Alexeev

무언가에 대한 참조를 원한다면 실제로 문자열이 아닌 참조를 사용해야합니다.
svick

이 답변은 실제 버그에 도움이되었습니다. 나는 Observable.Create()Observable을 빌드하는 데 사용 하고 있었고 여러 소스 Observables를 사용하여 연결했습니다 Subscribe(). 실수로 하나의 코드 경로에서 완료된 관찰 가능 항목을 전달했습니다. 다른 출처가 완전하지 않더라도 새로 만든 관찰 가능 항목을 완료했습니다. 내가 할 필요가 무엇인지 나에게 일에 나이를 툭 - 스위치를 Observable.Empty()위한 Observable.Never().
Olly

0

나는이 알고 방법 파티에 늦게,하지만 ...

인터페이스는 I Observable<T>하고 IObserver<T>있다 되지 수신 ... 그들이있는 거 핵심 유형의 일부 ...하지만 수신은이를 광범위하게 사용합니다.

원하는만큼 관찰자를 가질 수 있습니다. 관찰자가 여러 명일 것으로 예상되는 경우 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 );
  );
}

옵저버가 여러 옵저버 블에서 이벤트를 포착하는 경우 .Net 이벤트와 같이 이벤트 자체에 일종의 상관 정보가 있는지 확인할 수 있습니다 sender. 그것이 중요한지 아닌지는 당신에게 달려 있습니다. 올바르게 추론 한 것처럼 구워지지 않았습니다. (질문 3)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.