如何将轮询系统转换为Rx.Net IOobservable

本文关键字:Rx Net IOobservable 转换 系统 | 更新日期: 2023-09-27 17:54:10

我有一个游戏(基于MonoGame/XNA(,更新方法如下:

public void Update(GameTime gameTime)
{
    component.Update(gameTime);
}

我想将其转换为反应模式。我目前的解决方案是:

public void Initialize()
{
    updateSubject = new Subject<GameTime>();
    component = new Component();
    updateSubject.Subscribe((gameTime) => component.Update(gameTime));
}
public void Update(GameTime gameTime)
{
    updateSubject.OnNext(gameTime);
}

我是Rx的新手,所以我仍然在学习最好的做事方式。我读到应该避免使用Subject,而应该使用Observable.Create

Subject在这里合适吗?

在这种情况下,我如何使用Observable.Create

如何将轮询系统转换为Rx.Net IOobservable

您在这里面临的关键问题是您需要一个可观察的源。通常,您可以从事件、委托、任务、许多可观察扩展(如.Interval.Generate(和主题的各种来源创建可观察对象。

在你的情况下,你必须有一个源,你可以有代码,在你的可观察的外部,将值推送到。在这种情况下,主题是完全可以的,但你也可以使用委托。

如果你使用一个主题,那么你的代码就可以了。唯一的缺点是,您可以调用updateSubject.OnCompleted并完成可观察到的。

如果你想使用委托,那么你的代码可能看起来像这样:

private Action<GameTime> updateGameTime = null;
public void Initialize()
{
    component = new Component();
    Observable
        .FromEvent<GameTime>(a => updateGameTime += a, a => updateGameTime -= a)
        .Subscribe((gameTime) => component.Update(gameTime));
}
public void Update(GameTime gameTime)
{
    updateGameTime(gameTime);
}

通过这种方式,您对updateGameTime唯一能做的就是传入一个新的GameTime——您不能"意外"地结束序列。

现在,与Observable.Create相比,使用受试者的整个问题是状态问题。在你的代码中,你需要状态,所以主题是可以的。不过,总的来说,只要可能,最好封装状态——这就是Observable.Create为你所做的。

举个例子:

var i = -1;
var query =
    Observable
        .Range(0, 10).Select(x =>
        {
            i = -i * 2;
            return x * i;
        });

如果我订阅了两次这个可观察的序列,我会得到这两个序列:

(1(

0-416-48128-320768-17924096-9216

(2(

0-409616384-49152131072-327680786432-18350084194304-9437184

由于我使用了状态(即var i = -1;(,所以序列发生了变化。

如果我用Observable.Create编写代码,我可以避免这种状态:

var query =
    Observable
        .Create<int>(o =>
        {
            var i = -1;
            return
                Observable
                .Range(0, 10).Select(x =>
                {
                    i = -i * 2;
                    return x * i;
                })
                .Subscribe(o);
        });

它仍然是同一个查询,但状态是封装的,所以如果我现在订阅两次,我会得到:

(1(

0-416-48128-320768-17924096-9216

(2(

0-416-48128-320768-17924096-9216

在编写复杂的查询时,有时你可能会认为使用主题会更容易,而通常情况下,这就是出错的地方。在这种情况下,在使用主题之前,您应该始终尝试找到纯运算符方法。如果不能,则在Observable.Create中封装主题的使用。

在像你这样的时代,使用一个主题是可以的,因为你需要那种外部状态。

只是指出您的代码不必要地使用Rx。

public void Initialize()
{
    //updateSubject = new Subject<GameTime>();
    component = new Component();
    //updateSubject.Subscribe((gameTime) => component.Update(gameTime));
}
public void Update(GameTime gameTime)
{
    //updateSubject.OnNext(gameTime);
    component.Update(gameTime)
}

在这里,我删除了Subject,并直接调用componentUpdate方法来说明这一点。

也许你正在寻找一种私人的投票方式?在这种情况下,CCD_ 18可能是一个很好的起点。