反应式扩展:Zip操作员,但有所不同

本文关键字:有所不同 操作员 Zip 扩展 反应式 | 更新日期: 2023-09-27 18:25:55

这不是一个真实的例子(这段代码可能不会编译),但我正在努力让它比我实际遇到的问题简单一点。

假设我有一组图像:

private void IEnumerable<Image> GetImages()
{
    foreach (var filename in GetFilenames())
    {
         yield return Image.LoadFile(filename);
    }
}

我想展示由用户按下"空格"驱动的幻灯片:

var images = Observable.FromEvent(form, "KeyPress")
  .Where(e => e.KeyCode == KeyCode.Space)
  .Zip(GetImages.ToObservable(), (k, i) => i);

这种方法很有效。当按下空格时,它确实会发出下一个图像。问题是,它实际上是以全速加载它们的,所以它们会被缓冲并消耗大量内存(以及加载时的处理能力)。我可以将过滤后的按键输入GetImages并在那里进行压缩,但我不会保持GetImages的纯度。

有什么方法可以防止枚举。ToObservable()是否在不需要的情况下提前枚举?

另一个例子(这个将编译):

var observable = 
    Observable.Interval(TimeSpan.FromSeconds(1))
    .Zip(
        Observable.Range(0, 1000000).Do(x => Console.WriteLine("produced {0}", x)), 
        (_, v) => v
    );
var subscription = observable.Subscribe(x => Console.WriteLine("consumed {0}", x));
Console.WriteLine("Press <enter>...");
Console.ReadLine();

它会产生很多"生产"(提前),但每秒只有一个"消耗"。

反应式扩展:Zip操作员,但有所不同

Dave坚持从IEnumerable<T>中提取图像的想法是可靠的,但您可以更容易地实现相同的目标-只需从代码中删除ToObservable()即可:

var images = Observable.FromEvent(form, "KeyPress")
                       .Where(e => e.KeyCode == KeyCode.Space)
                       .Zip(GetImages() /* No ToObservable() here! */, (k, i) => i);

Zip的这种过载将在您按键时使图像可枚举。

不如从枚举器中提取?

var keys = Observable.FromEventPattern<KeyPressEventArgs>(form, "KeyPress");
var slides = (from key in keys
              where key.EventArgs.KeyCode == KeyCode.Space
              select key)
             .Publish(spaces => 
              {
                var images = GetImages().GetEnumerator();
                return spaces.TakeUntil(from _ in spaces
                                        where !images.MoveNext()
                                        select Unit.Default)
                             .Select(_ => images.Current);
              });