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

时间:2016-07-03 15:13:50

标签: c# xna system.reactive monogame rx.net

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

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

我想将此转换为Reactive模式。我目前的解决方案是:

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

2 个答案:

答案 0 :(得分:7)

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

在您的情况下,您必须拥有一个源代码,您可以拥有代码,在您的observable外部,将值推送到。在这种情况下,主题非常好,但您也可以使用代理。

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

如果您想使用委托,那么您的代码可能如下所示:

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 - 你不能意外地&#34;结束序列。

现在使用主题与Observable.Create的整个问题是状态之一。在您的代码中,您需要状态,因此主题是可以的。一般来说,尽管如此,建议封装状态 - 以及Observable.Create为您做的事情。

举个例子:

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

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

(1)

0 
-4 
16 
-48 
128 
-320 
768 
-1792 
4096 
-9216 

(2)

0 
-4096 
16384 
-49152 
131072 
-327680 
786432 
-1835008 
4194304 
-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 
-4 
16 
-48 
128 
-320 
768 
-1792 
4096 
-9216 

(2)

0 
-4 
16 
-48 
128 
-320 
768 
-1792 
4096 
-9216 

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

像你这样的时候,使用某个主题很好,因为你需要那个外部状态。

答案 1 :(得分:0)

指出你代码unnessecarily使用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,并直接致电component Update方法,以说明这一点。

也许您正在寻找私人投票方法?在这种情况下,Observable.Interval可能是一个很好的起点。