在Rx中,为什么何时给我过时的元素

时间:2015-04-19 18:42:09

标签: c# system.reactive

我有一个包含多种通知类型的流。一种通知类型包含有关当前文件的信息并连续发送。当用户单击按钮时会发出另一种类型。这两个通知都在一个流中。

当用户点击按钮时,我想用当前文件做一些事情。我将源流分成两个流并尝试结合"按钮点击"使用最新的"当前文件"通知。

static IObservable<DoSomething> DoSomethingWithFile(IObservable<object> stream)
{
  var buttonClick = stream.OfType<ButtonClick>();
  var file = stream.OfType<CurrentFile>();

  var match = buttonClick
    .And(file)
    .Then((command, f) => new DoSomething());

  return Observable.When(match);
}

这是理想的大理石图:

File    1-1-1-1-2-2-2-3-3-3-3
Button  ----x-----------x----

Desired ----1-----------3----
            x           x

代码通常有效,但如果当前文件信息发生变化,则只会发生几次。而不是上面的大理石图,我得到了这个:

File    1-1-1-1-2-2-2-3-3-3-3
Button  ----x-----------x----

Actual  ----1-----------2----
            x           x

CombineLatest在这种情况下不起作用,因为在每个新文件中它都会向目标序列发出通知,尽管用户没有点击按钮。

我知道这个问题非常笼统,但当然我们正在谈论a real project: - )

2 个答案:

答案 0 :(得分:2)

所以AndPlanPattern对我来说都是新手。很酷的东西。基于我对And的实验,它似乎缓冲了快速观察值,直到慢速值发出一个值,此时它返回来自快速可观察量的第一个值和来自慢速可观察量的最新值。这可以解释你所看到的行为。我在MSDN上找不到任何文档,详细解释And如何工作,遗憾的是,这可以证实我认为我所看到的内容。

我认为这会为您提供所需的结果:

static IObservable<DoSomething> DoSomethingWithFile(IObservable<object> stream)
{
    var buttonClick = stream.OfType<ButtonClick>();
    var file = stream.OfType<CurrentFile>();

    return
        file
        .Select(f =>
            buttonClick
            .Select(b => new { File = f, ButtonClick = b })
            //.Take(1)
            // Include this if you only want to register 
            // one button click per file change
            )
        .Switch()
        .Select(x => new DoSomething());
}

答案 1 :(得分:2)

Zip,And或CombineLatest是正确的运营商。您在文件流上使用DistinctUntilLatest的更改是一个良好的开端。但是,在上面的示例中,即使使用DistinctUntilChanged,您仍会收到(2,x)中任何一个的通知。

Zip将为您推送两个流的每个组合不同值的通知。因此,您无法单独使用Zip跳过文件2的通知。并将有相同的行为。 CombineLatest还会给你2通知。

如果您想要上面绘制的大理石图,只有在单击按钮时才从文件流中获取最新值,您可以使用以下方法:

file.CombineLatest(buttonClick, (file, button) => new { file, button })
    .DistinctUntilChanged(data => data.button)

考虑这个例子,使用慢流和快速流。每次慢流时,我们都会从快速流中获取最新值:

var fast = Observable.Interval(TimeSpan.FromSeconds(1));
var slow = Observable.Interval(TimeSpan.FromSeconds(1.5));

fast.CombineLatest(slow, (fastValue, slowValue) => new { fastValue, slowValue })
    .DistinctUntilChanged(data => data.slowValue)
    .Dump();

如果您注释掉DistinctUntilChanged,那么每次快速来源滴答时,您都会看到它产生一个值。

RxJS有一个运营商为我们做了这个名为withLatestFrom的运营商,但不幸的是,它不在.Net版本中。你可以做button.withLatestFrom(文件)并从那里开始。