如何知道下一个值来自哪个可观测值?

时间:2016-07-20 11:05:48

标签: c# system.reactive

让我说我有这样的事情:

var observable = observable1
                 .Merge(observable2)
                 .Merge(observable3);

var subscription = observable.Subscribe(ValueHandler);

...

public void ValueHandler(string nextValue)
{
  Console.WriteLine($"Next value {nextValue} produced by /* sourceObservable */");
}

如果没有将该引用与每个可观察实现中的值一起添加,是否有一种方法可以从observable1observable2observable3中获取源代码,从而生成下一个值?

2 个答案:

答案 0 :(得分:2)

不,这与Merge的设计完全相反。 Merge旨在获取多个流并将其视为一个流。如果您想要某种方式单独处理它们,请使用不同的运算符。

修改

对于传递源的操作员,简短的回答是否定的。 Rx是关于对消息的反应,来源是无关紧要的。我甚至不确定你是否可以在概念上定义Rx中的“来源”:

var observable1 = Observable.Interval(TimeSpan.FromSeconds(1)).Take(5);
var observable2 = observable1.Select(a => a.ToString());
var subscription = observable2.Subscribe(s => s.Dump());

订阅源observable1observable2或某种指向系统时钟的指针?

如果您要将输入的邮件分开,那么您可以使用Select,如下所示:

var observable = observable1.Select(o => Tuple.Create("observable1", o))
  .Merge(observable2.Select(o => Tuple.Create("observable2", o)))
  .Merge(observable3.Select(o => Tuple.Create("observable3", o)));

如果这太乱了,那么你可以轻松地制作一个扩展方法来清理它。

我还要补充一点,你在答案中发布的代码不是很像Rx。一般准则是避免直接实施IObservableSchool可以更简洁地重写如下:

    public class School
    {
        //private Subject<Student> _subject = null;
        private readonly ISubject<Student> _applicationStream = null;

        public static readonly int MaximumNumberOfSeats = 100;

        public string Name { get; set; }

        public School(string name)
            : this(name, new Subject<Student>())
        {
        }

        public School(string name, ISubject<Student> applicationStream )
        {
            Name = name;
            _applicationStream = applicationStream;
        }

        public void AdmitStudent(Student s)
        {
            _applicationStream.OnNext(s);
        }

        public IObservable<Student> ApplicationStream()
        {
            return _applicationStream;
        }

        public IObservable<Student> AcceptedStream()
        {
            return _applicationStream
                .SelectMany(s => s != null ? Observable.Return(s) : Observable.Throw<Student>(new ArgumentNullException("student")))
                .Distinct()
                .Take(MaximumNumberOfSeats);
        }
    }

通过这种方式,您可以订阅所有应用程序,接受,如果您愿意,还可以订阅拒绝等等。您的状态也较少(无List<Student>),理想情况下您甚至可以删除{{ 1}}并将其转换为在某处传递的Observable。

答案 1 :(得分:0)

不,没有任何开箱即用的信息可以为我们提供有关哪个observable产生价值的信息。

那是因为这是一个实现细节。

如果Rx的设计者必须提供这些附加信息,他们只能通过对TSource泛型类型参数施加某种限制来实现。这不会很好。这是值处理程序可以知道谁生成了值的唯一方法。

因此,获取此信息的责任在于使用Rx实现开发人员。

为了举一个例子,让我们说你有一个School课程,这是一个可以观察到这样的学生:

using System;
using System.Collections.Generic;
using System.Reactive.Subjects;


namespace SchoolManagementSystem
{
    public class Student
    {
        public Student(string name)
        {
            Name = name;
        }

        public string Name { get; set; }
    }

    public class School : IObservable<Student>
    {
        private List<Student> _students;
        private Subject<Student> _subject = null;

        public static readonly int MaximumNumberOfSeats = 100;

        public string Name { get; set; }

        public School(string name)
        {
            Name = name;
            _students = new List<Student>();
            _subject = new Subject<Student>();
        }

        public void AdmitStudent(Student student)
        {
            if (student == null)
            {
                var ex = new ArgumentNullException("student");
                _subject.OnError(ex);
                throw ex;
            }

            try
            {
                if (_students.Count == MaximumNumberOfSeats)
                {
                    _subject.OnCompleted();

                    return;
                }

                if (!_students.Contains(student))
                {    
                    _students.Add(student);

                    _subject.OnNext(student);
                }
            }
            catch(Exception ex)
            {
                _subject.OnError(ex);
            }
        }

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

客户端代码如下:

using SchoolManagementSystem;
using System;
using System.Reactive.Linq;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var school1 = new School("School 1");
            var school2 = new School("School 2");
            var school3 = new School("School 3");

            var observable = school1
                .Merge(school2)
                .Merge(school3);

            var subscription = observable
                .Subscribe(PrintStudentAdmittedMessage, PrintNoMoreStudentsCanBeAdmittedMessage);

            school1.FillWithStudents(100);
            school2.FillWithStudents(102);
            school3.FillWithStudents(101);

            Console.WriteLine("Press any key to stop observing and to exit the program.");
            Console.ReadKey();
            subscription.Dispose();
        }

        static void PrintStudentAdmittedMessage(Student student)
        {
            Console.WriteLine($"Student admitted: {student}");
        }

        static void PrintNoMoreStudentsCanBeAdmittedMessage()
        {
            Console.WriteLine("No more students can be admitted.");
        }
    }
}

然后,要让客户知道学生入学的哪所学校,您必须更改TSource的{​​{1}}类型。在这种情况下,您必须更改IObservable<TSource>School的事实。

然而,改变这与域模型的语义相违背。因此,解决这个问题的方法是在学生内部包含对IObservable<Student>的引用,如下所示:

School

然后你会改变using System; using System.Collections.Generic; namespace SchoolManagementSystem { public class Student { public Student(string name) { Name = name; } // Add this new property so you get this information // in the value handler public School School { get; set; } public string Name { get; set; } public override string ToString() { return string.Format($"({School.Name}: {Name})"); } } } 课程的AdmitStudent方法,以表明学生被录取的学校,如下:

School