C# 与多个IEnumerable一起使用时,Observable.Subscribe的行为

C# 与多个IEnumerable一起使用时,Observable.Subscribe的行为,c#,system.reactive,C#,System.reactive,我正试图从两个数组(IEnumerables)创建一个IObservable。我试图避免显式迭代数组并调用observer.OnNext。我遇到了扩展方法,乍一看,它似乎就是我所需要的。然而,它并没有像我预期的那样起作用,我不知道为什么 以下代码是一个示例: class Program { static void Main(string[] args) { var observable = Observable.Create<char>(obser

我正试图从两个数组(
IEnumerable
s)创建一个
IObservable
。我试图避免显式迭代数组并调用
observer.OnNext
。我遇到了扩展方法,乍一看,它似乎就是我所需要的。然而,它并没有像我预期的那样起作用,我不知道为什么

以下代码是一个示例:

  class Program
  {
    static void Main(string[] args)
    {
      var observable = Observable.Create<char>(observer =>
        {
          var firstBytes = new[] {'A'};
          var secondBytes = new[] {'Z', 'Y'};
          firstBytes.Subscribe(observer);
          secondBytes.Subscribe(observer);

          return Disposable.Empty;
        }
      );

      observable.Subscribe(b => Console.Write(b));
    }
  }
类程序
{
静态void Main(字符串[]参数)
{
var observable=observable.Create(观察者=>
{
var firstBytes=new[]{'A'};
var secondBytes=new[]{'Z','Y'};
订阅(观察者);
订阅(观察者);
返回一次性。空;
}
);
observeable.Subscribe(b=>Console.Write(b));
}
}
它的输出是“AZ”,而不是我预期的“AZY”。现在,如果我在
firstBytes
之前订阅
secondBytes
,则输出为“ZAY”!这似乎表明它正在步骤中枚举这两个数组,这可以解释“AZ”输出


无论如何,我完全不知道它为什么会这样,我希望人们能够提供任何见解。

因为你订阅的是两个观测值,而不是两个观测值的串联的单个观测值,有两个可能的源可以调用观察者的
OnComplete
方法。由于第一个数组较短,它在发出第一个项目后完成,而观察者由于收到完成通知而取消订阅

正确的方法是将两个序列组合成一个序列,然后订阅该序列:

var observable = Observable.Create<char>(observer =>
{
    var firstBytes = new[] { 'A' };
    var secondBytes = new[] { 'Z', 'Y' };

    return firstBytes.Concat(secondBytes).Subscribe(observer);
});

observable.Subscribe(Console.Write);
var observable=observable.Create(observable=>
{
var firstBytes=new[]{'A'};
var secondBytes=new[]{'Z','Y'};
返回firstBytes.Concat(secondBytes.Subscribe(observer);
});
observeable.Subscribe(Console.Write);

锁定步骤迭代行为的原因可以通过以下方式来解释:在调度程序操作中调用
e.MoveNext
来实现锁定步骤迭代行为。如果成功,则会发出该值,并将新的调度程序操作排入队列,以从枚举中读取下一个值

由于您订阅了两个枚举,并且没有为订阅指定任何特定的计划程序,因此默认的迭代计划程序将用于这些操作(由
SchedulerDefaults.iteration
定义),这些操作默认在当前线程上运行。这意味着枚举操作将排队,以便在当前订阅操作完成后运行。这会导致枚举操作交错-类似这样

  • firstBytes.Subscribe()->队列枚举操作
  • secondBytes.Subscribe()->队列枚举操作
  • 调用firstBytes.MoveNext()->OnNext(“A”)->队列下一个枚举操作
  • 调用secondBytes.MoveNext()->OnNext(“Z”)->队列下一个枚举操作
  • 调用firstBytes.MoveNext()->OnCompleted()
  • 调用secondBytes.MoveNext()->OnNext(Y)->队列下一个枚举操作
  • 调用secondBytes.MoveNext()->OnCompleted()

  • 观察者在步骤5接收OnCompleted()通知,因此忽略其余的secondBytes枚举步骤。如果您已返回了订阅可处置文件,则此时第二次枚举将被取消。

    仍在试图找出其行为方式的原因,但如果您只需要修复:
    return firstBytes.Concat(secondBytes.Subscribe(observer))。只是一个小小的提示-如果你发现自己返回了
    一次性。空的
    ,那么你就做错了。@Enigmativity-谢谢你的建议-在这种情况下,它更多的是示例简单性的副产品(或者是否有更合适的返回)。@cristobalito-是的,你会返回
    新的CompositeDisposable(firstBytes.Subscribe(observer),secondBytes.Subscribe(observer))
    @Enigmativity-nice-one-thanksMakes-sense可以发誓我曾尝试过Concat方法(在我更复杂的用例中)。感谢您的帮助。尽管-that
    Concat
    是一个
    IEnumerable.Concat
    。为了保持在
    IObservable
    land中,它可能应该是
    firstBytes.ToObservable().Concat(secondBytes.ToObservable())
    @cristobalito至少在这种情况下,它在功能上并没有什么区别。嗯,我不会说“正确的方法”OP中的整个示例都是假的,而且是Rx for Rx。如果知道OP在源为IEnumerable时为什么要使用Rx,那就太好了。Hi Lee-同意示例是假的,但与我试图解决的问题相比,它是一个简化的示例。无论如何,我认为您是正确的,尝试将解决方案穿入Rx不是最好的选择前进的道路。正如下面的答案中似乎遗漏了它,但我认为你的问题暗示了这一点,你所需要的只是
    firstBytes.Concat(secondBytes)
    。虽然这只会给你一个
    IEnumerable
    ,但你仍然可以订阅IEnumerables。如果你真的想要,你可以点击
    .ToObservable()
    在它的末尾。但是不需要OP中的
    可观察。创建