合并RX中的多个自定义可观察量

时间:2016-04-14 19:24:29

标签: linq system.reactive reactive-programming

尝试使用RX为多个发布商发送通知的系统建模。

我有两个自定义接口ITopicObservable和ITopicObserver来模拟实现类除了IObservable和IObserver接口之外还有其他属性和方法的事实。

我遇到的问题是我的想法是我应该能够将多个可观察对象组合在一起,将它们合并在一起并订阅观察者以提供来自所有合并的可观察对象的更新。但是代码中包含"问题"注释会抛出无效的强制转换异常。

用例是一些独立的传感器,每个传感器监测盒子中的温度,例如将所有报告汇总到一个温度报告,然后由温度健康监测器订阅。

我在这里缺少什么?或者有更好的方法来使用RX实现场景吗?

以下代码

using System;
using System.Reactive.Linq;
using System.Collections.Generic;

namespace test
{
class MainClass
{
    public static void Main (string[] args)
    {
        Console.WriteLine ("Hello World!");
        var to = new TopicObserver ();
        var s = new TopicObservable ("test");

        var agg = new AggregatedTopicObservable ();
        agg.Add (s);

        agg.Subscribe (to);
    }
}

public interface ITopicObservable<TType>:IObservable<TType>
{
    string Name{get;}
}

public class TopicObservable:ITopicObservable<int>
{
    public TopicObservable(string name)
    {
        Name = name;
    }
    #region IObservable implementation
    public IDisposable Subscribe (IObserver<int> observer)
    {
        return null;
    }
    #endregion
    #region ITopicObservable implementation
    public string Name { get;private set;}

    #endregion
}

public class AggregatedTopicObservable:ITopicObservable<int>
{
    List<TopicObservable> _topics;
    private ITopicObservable<int> _observable;
    private IDisposable _disposable;

    public AggregatedTopicObservable()
    {
        _topics = new List<TopicObservable>();
    }

    public void Add(ITopicObservable<int> observable)
    {
        _topics.Add ((TopicObservable)observable);
    }

    #region IObservable implementation
    public IDisposable Subscribe (IObserver<int> observer)
    {
        _observable = (ITopicObservable<int>)_topics.Merge ();

        _disposable = _observable.Subscribe(observer);

        return _disposable;
    }
    #endregion
    #region ITopicObservable implementation
    public string Name { get;private set;}
    #endregion

}



public interface ITopicObserver<TType>:IObserver<TType>
{
    string Name{get;}
}

public class TopicObserver:ITopicObserver<int>
{
    #region IObserver implementation
    public void OnNext (int value)
    {
        Console.WriteLine ("next {0}", value);
    }
    public void OnError (Exception error)
    {
        Console.WriteLine ("error {0}", error.Message);
    }
    public void OnCompleted ()
    {
        Console.WriteLine ("finished");
    }
    #endregion
    #region ITopicObserver implementation
    public string Name { get;private set;}
    #endregion

}
}

2 个答案:

答案 0 :(得分:2)

我首先想到的是,你不应该实现IObservable<T>,你应该通过将其作为属性或方法的结果来构建它。

第二个想法是,Rx中的运算符擅长将多个序列合并/聚合在一起。 你应该赞成使用它们。

第三,与第一个类似,您通常不实现IObserver<T>,您只需订阅可观察序列并为每个回调提供代理(OnNextOnErrorOnComplete

所以你的代码基本上缩减为

Console.WriteLine("Hello World!");
var topic1 = TopicListener("test1");
var topic2 = TopicListener("test2");

topic1.Merge(topic2)
    .Subscribe(
    val => { Console.WriteLine("One of the topics published this value {0}", val);},
    ex => { Console.WriteLine("One of the topics errored. Now the whole sequence is dead {0}", ex);},
    () => {Console.WriteLine("All topics have completed.");});

TopicListener(string)只是一个返回IObservable<T>的方法。 TopicListener(string)方法的实施最有可能使用Observable.Create

查看基于主题的消息传递系统上的Rx映射示例可能会有所帮助。 有一个示例说明如何在此处https://github.com/LeeCampbell/RxCookbook/blob/master/IO/Comms/TibRvSample.linq

对Rx over TibRv主题进行分层

答案 1 :(得分:1)

您正在使用的.Merge(...)运算符的签名是:

IObservable<TSource> Merge<TSource>(this IEnumerable<IObservable<TSource>> sources)

.Merge()返回的实际类型为:

System.Reactive.Linq.ObservableImpl.Merge`1[System.Int32]

...所以应该相当清楚,调用(ITopicObservable<int>)_topics.Merge();会失败。

李建议不实施IObservable<>IObserver<>的建议是正确的。它会导致上述错误。

如果你不得不这样做,我会这样做:

public interface ITopic
{
    string Name { get; }
}

public interface ITopicObservable<TType> : ITopic, IObservable<TType>
{ }

public interface ITopicSubject<TType> : ISubject<TType>, ITopicObservable<TType>
{ }

public interface ITopicObserver<TType> : ITopic, IObserver<TType>
{ }

public class Topic
{
    public string Name { get; private set; }

    public Topic(string name)
    {
        this.Name = name;
    }
}

public class TopicSubject : Topic, ITopicSubject<int>
{
    private Subject<int> _subject = new Subject<int>();

    public TopicSubject(string name)
        : base(name)
    { }

    public IDisposable Subscribe(IObserver<int> observer)
    {
        return _subject.Subscribe(observer);
    }

    public void OnNext(int value)
    {
        _subject.OnNext(value);
    }

    public void OnError(Exception error)
    {
        _subject.OnError(error);
    }

    public void OnCompleted()
    {
        _subject.OnCompleted();
    }
}

public class AggregatedTopicObservable : Topic, ITopicObservable<int>
{
    List<ITopicObservable<int>> _topics = new List<ITopicObservable<int>>();

    public AggregatedTopicObservable(string name)
        : base(name)
    { }

    public void Add(ITopicObservable<int> observable)
    {
        _topics.Add(observable);
    }

    public IDisposable Subscribe(IObserver<int> observer)
    {
        return _topics.Merge().Subscribe(observer);
    }
}

public class TopicObserver : Topic, ITopicObserver<int>
{
    private IObserver<int> _observer;

    public TopicObserver(string name)
        : base(name)
    {
        _observer =
            Observer
                .Create<int>(
                    value => Console.WriteLine("next {0}", value),
                    error => Console.WriteLine("error {0}", error.Message),
                    () => Console.WriteLine("finished"));
    }

    public void OnNext(int value)
    {
        _observer.OnNext(value);
    }
    public void OnError(Exception error)
    {
        _observer.OnError(error);
    }
    public void OnCompleted()
    {
        _observer.OnCompleted();
    }
}

运行它:

var to = new TopicObserver("watching");
var ts1 = new TopicSubject("topic 1");
var ts2 = new TopicSubject("topic 2");

var agg = new AggregatedTopicObservable("agg");

agg.Add(ts1);
agg.Add(ts2);

agg.Subscribe(to);

ts1.OnNext(42);
ts1.OnCompleted();

ts2.OnNext(1);
ts2.OnCompleted();

给出了:

next 42
next 1
finished

但除了能给所有名字(我不确定它有什么帮助)之外,你总能做到这一点:

var to =
    Observer
        .Create<int>(
            value => Console.WriteLine("next {0}", value),
            error => Console.WriteLine("error {0}", error.Message),
            () => Console.WriteLine("finished"));

var ts1 = new Subject<int>();
var ts2 = new Subject<int>();

var agg = new [] { ts1, ts2 }.Merge();

agg.Subscribe(to);

ts1.OnNext(42);
ts1.OnCompleted();

ts2.OnNext(1);
ts2.OnCompleted();

相同的输出,没有接口和类。

甚至还有一种更有趣的方式。试试这个:

var to =
    Observer
        .Create<int>(
            value => Console.WriteLine("next {0}", value),
            error => Console.WriteLine("error {0}", error.Message),
            () => Console.WriteLine("finished"));

var agg = new Subject<IObservable<int>>();

agg.Merge().Subscribe(to);

var ts1 = new Subject<int>();
var ts2 = new Subject<int>();

agg.OnNext(ts1);
agg.OnNext(ts2);

ts1.OnNext(42);
ts1.OnCompleted();

ts2.OnNext(1);
ts2.OnCompleted();

var ts3 = new Subject<int>();

agg.OnNext(ts3);

ts3.OnNext(99);
ts3.OnCompleted();

这会产生:

next 42
next 1
next 99

它允许您在合并后添加新的源可观察量!