27

言及されたエニグマティビティに関する質問へのこの回答Subject<T>では:

余談ですが、件名を使用しないようにしてください。一般的なルールは、サブジェクトを使用している場合、何か間違ったことをしているということです。

私はよくサブジェクトをIObservableプロパティのバッキング フィールドとして使用します。これは、Rx の前の時代にはおそらく .NET イベントだったでしょう。たとえば、次のようなものの代わりに

public class Thing
{
    public event EventHandler SomethingHappened;

    private void DoSomething()
    {
        Blah();
        SomethingHappened(this, EventArgs.Empty);
    }
}

私はするかもしれません

public class Thing
{
    private readonly Subject<Unit> somethingHappened = new Subject<Unit>();
    public IObservable<Unit> SomethingHappened
    {
        get { return somethingHappened; }
    }

    private void DoSomething()
    {
        Blah();
        somethingHappened.OnNext(Unit.Default);
    }
}

それで、私がSubjectこの種のことをする正しい方法は何でしょうか?それとも、Rx コードによって消費される場合でも、インターフェイスで .NET イベントを使用することに固執する必要がありますFromEventPatternか (おそらく)。

Subjectまた、このように使用することが悪い考えである理由についてもう少し詳しく説明すると役立ちます。

更新: この質問をもう少し具体的にSubject<T>するために、Rx 以外のコード (おそらく他のレガシー コードを使用している) から Rx の世界に入る方法として を使用することについて話しています。したがって、次のようなものです:

class MyVolumeCallback : LegacyApiForSomeHardware
{
    private readonly Subject<int> volumeChanged = new Subject<int>();

    public IObservable<int> VolumeChanged
    {
        get
        {
            return volumeChanged.AsObservable();
        }
    }

    protected override void UserChangedVolume(int newVolume)
    {
        volumeChanged.OnNext(newVolume);
    }
}

イベントを使用する代わりに、LegacyApiForSomeHardware 型を使用すると、「これが起こった」という通知を取得する方法として仮想メソッドをオーバーライドできます。

4

4 に答える 4

17

1 つには、SomethingHappened を ISubject にキャストし直し、外部から何かをフィードすることができます。少なくとも、AsObservable下にあるオブジェクトの主題性を隠すために適用してください。

また、コールバックのサブジェクト ブロードキャストは、厳密には .NET イベント以上のことを行いません。たとえば、1 つのオブザーバーがスローした場合、チェーン内の次のオブザーバーは呼び出されません。

    static void D()
    {
        Action<int> a = null;

        a += x =>
        {
            Console.WriteLine("1> " + x);
        };

        a += x =>
        {
            Console.WriteLine("2> " + x);

            if (x == 42)
                throw new Exception();
        };

        a += x =>
        {
            Console.WriteLine("3> " + x);
        };

        a(41);
        try
        {
            a(42);  // 2> throwing will prevent 3> from observing 42
        }
        catch { }
        a(43);
    }

    static void S()
    {
        Subject<int> s = new Subject<int>();

        s.Subscribe(x =>
        {
            Console.WriteLine("1> " + x);
        });

        s.Subscribe(x =>
        {
            Console.WriteLine("2> " + x);

            if (x == 42)
                throw new Exception();
        });

        s.Subscribe(x =>
        {
            Console.WriteLine("3> " + x);
        });

        s.OnNext(41);
        try
        {
            s.OnNext(42);  // 2> throwing will prevent 3> from observing 42
        }
        catch { }
        s.OnNext(43);
    }

一般に、すべての On* 呼び出しを保護しない限り、オブザーバーがスローすると、呼び出し元は無効になります (ただし、上記のように、例外を勝手に飲み込まないでください)。これは、マルチキャスト デリゲートでも同じです。例外はあなたに戻ってきます。

ほとんどの場合、Observable.Create を使用して新しいシーケンスを構築するなど、サブジェクトなしでやりたいことを達成できます。このようなシーケンスには、複数のサブスクリプションから生じる「オブザーバー リスト」がありません。各オブザーバーには独自の「セッション」(コールドオブザーバブルモデル)があるため、オブザーバーからの例外は、正方形の真ん中で自分自身を爆破するのではなく、限られたエリアでの自殺コマンドにすぎません.

基本的に、サブジェクトはリアクティブ クエリ グラフのエッジで使用するのが最適です (データをフィードする別のパーティがアドレス指定できる必要があるイングレス ストリーム用ですが、これには通常の .NET イベントを使用し、FromEvent* を使用してそれらを Rx にブリッジすることもできます)。メソッド) およびリアクティブ クエリ グラフ内でサブスクリプションを共有するため (サブジェクトを使用して、変装したマルチキャスト呼び出しであるパブリッシュ、リプレイなどを使用)。サブジェクトを使用する危険性の 1 つは、オブザーバー リストとメッセージの潜在的な記録のために非常にステートフルなサブジェクトを使用することです。99.999% の確率で、そのような物語は悲しい結末を迎えます。

于 2012-08-21T18:52:34.790 に答える
12

Rx フォーラムの回答で、( Rxxの) Dave Sexton は、何かに対する回答の一部として次のように述べています。

サブジェクトは、Rx のステートフル コンポーネントです。これらは、フィールドまたはローカル変数としてイベントのようなオブザーバブルを作成する必要がある場合に役立ちます。

この質問で何が起こっているのか正確にはどちらですか? 彼はまた、件名を使用するか、件名を使用しないかに関する詳細なフォローアップのブログ投稿を書きました? これは次のように締めくくられます。

いつ件名を使用する必要がありますか?

次のすべてに該当する場合:

  • オブザーバブルまたはオブザーバブルに変換できるものはありません。
  • ホットオブザーバブルが必要です。
  • オブザーバブルのスコープはタイプです。
  • 同様のイベントを定義する必要はなく、同様のイベントはまだ存在しません。

その場合、なぜ主語を使用する必要があるのでしょうか。

仕方がないから!

したがって、「このようにサブジェクトを使用することが悪い考えである理由の詳細」という内なる質問に答える-それは悪い考えではありません。これは、サブジェクトを使用していた数少ない場所の1つであり、正しい方法です。

于 2012-11-27T16:11:07.760 に答える
2

Enigmativityについて直接話すことはできませんが、それは非常に低レベルであり、実際に直接使用する必要がないためだと思います。Subject<T>クラスによって提供されるすべてのものは、 System.Reactive.Linq名前空間のクラスを使用して実現できます。

Subject<T>ドキュメントから例をとると:

Subject<string> mySubject = new Subject<string>();

//*** Create news feed #1 and subscribe mySubject to it ***//
NewsHeadlineFeed NewsFeed1 = new NewsHeadlineFeed("Headline News Feed #1");
NewsFeed1.HeadlineFeed.Subscribe(mySubject);

//*** Create news feed #2 and subscribe mySubject to it ***//
NewsHeadlineFeed NewsFeed2 = new NewsHeadlineFeed("Headline News Feed #2");
NewsFeed2.HeadlineFeed.Subscribe(mySubject);

これは、クラスのMerge拡張メソッドを使用して簡単に実現できます。Observable

IObservable<string> feeds = 
    new NewsHeadlineFeed("Headline News Feed #1").HeadlineFeed.Merge(
    new NewsHeadlineFeed("Headline News Feed #2").HeadlineFeed);

その後、通常どおりにサブスクライブできます。を使用Subject<T>すると、コードがより複雑になります。を使用する場合Subject<T>は、拡張メソッドが失敗するオブザーバブルの非常に低レベルの処理を実行する必要があります。

于 2012-08-21T14:52:08.570 に答える
2

単純な1回限りのイベントを持つクラスの1つのアプローチはToObservable、イベントに基づいて意味のあるコールドオブザーバブルを作成するメソッドを提供することです。これは、Observableファクトリメソッドを使用するよりも読みやすく、Rxを使用しない開発者がAPIを使用できるようにします。

    public IObservable<T> ToObservable()
    {
        return Observable.Create<T>(observer =>
        {
            Action notifier = () =>
            {
                switch (Status)
                {
                    case FutureStatus.Completed:
                        observer.OnNext(Value);
                        observer.OnCompleted();
                        break;

                    case FutureStatus.Cancelled:
                        observer.OnCompleted();
                        break;

                    case FutureStatus.Faulted:
                        observer.OnError(Exception);
                        break;                        
                }
            };

            Resolve += notifier;

            return () => Resolve -= notifier;

        });
    }
于 2012-08-22T08:10:57.200 に答える