简体   繁体   English

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

[英]How do I turn a polling system into an Rx.Net IObservable?

I have a game (based on MonoGame / XNA) with an update method like so: 我有一个游戏(基于MonoGame / XNA)和更新方法,如下所示:

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

I would like to convert this to the Reactive pattern. 我想将其转换为Reactive模式。 My current solution is: 我目前的解决方案是:

public void Initialize()
{
    updateSubject = new Subject<GameTime>();

    component = new Component();

    updateSubject.Subscribe((gameTime) => component.Update(gameTime));
}

public void Update(GameTime gameTime)
{
    updateSubject.OnNext(gameTime);
}

I am new to Rx so I am still learning the best way of doing things. 我是Rx的新手,所以我仍然在学习最好的做事方式。 I read that Subject should be avoided and Observable.Create should be used instead. 我读到应该避免使用Subject而应该使用Observable.Create

Is Subject appropriate here? 这里的Subject是否合适?

How could I use Observable.Create in this case? 在这种情况下我怎么能使用Observable.Create

The key issue you're facing here is that you need a source for your observable. 您在这里遇到的关键问题是您需要一个可观察源。 In general you can create observables from a variety of sources from events, delegates, tasks, many of the observable extensions (like .Interval or .Generate ), and subjects. 通常,您可以从事件,委托,任务,许多可观察扩展(如.Interval.Generate )和主题等各种来源创建可观察对象。

In your case you must have a source that you can have code, external to your observable, push values to. 在您的情况下,您必须拥有一个源代码,您可以拥有代码,在您的observable外部,将值推送到。 In this case a subject is perfectly fine, but you could just use a delegate also. 在这种情况下,主题完全没问题,但您也可以使用代理。

If you use a subject then your code is fine. 如果您使用主题,那么您的代码就可以了。 The only downside is that you could call updateSubject.OnCompleted and finish the observable. 唯一的缺点是你可以调用updateSubject.OnCompleted并完成observable。

If you want to use a delegate then your code could look like this: 如果您想使用委托,那么您的代码可能如下所示:

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);
}

In this way the only thing you can do with updateGameTime is pass in a new GameTime - you can't "accidentally" end the sequence. 通过这种方式,你可以用updateGameTime做的唯一事情是传入一个新的GameTime - 你不能“意外地”结束序列。

Now the whole issue with using subjects versus Observable.Create is one of state. 现在使用主题与Observable.Create的整个问题是状态之一。 In your code you need state, so a subject is OK. 在您的代码中,您需要状态,因此主题是可以的。 In general though, and whenever possible, it is advisable to encapsulate state - and that's what Observable.Create does for you. 通常,尽管如此,建议封装状态 - 这就是Observable.Create为您所做的事情。

Take this example: 举个例子:

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

If I subscribe to this observable twice I get these two sequences: 如果我订阅这个可观察的两次,我得到这两个序列:

(1) (1)

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

(2) (2)

0 
-4096 
16384 
-49152 
131072 
-327680 
786432 
-1835008 
4194304 
-9437184

The sequence changes because I used state (ie var i = -1; ). 序列发生变化,因为我使用了状态(即var i = -1; )。

Had I written the code with Observable.Create I could avoid this state: 如果我用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);
        });

It is still the same query, but the state is encapsulated, so if I subscribe twice now I get: 它仍然是相同的查询,但状态是封装的,所以如果我现在订阅两次,我得到:

(1) (1)

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

(2) (2)

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

There are times when writing complex queries that you might think that using a subject would make it much easier, and in general, that's where mistakes occur. 有时编写复杂的查询时,您可能会认为使用主题会使其变得更容易,而且通常情况下,这就是错误发生的地方。 You should always try to find a pure operator approach before using subjects in this case. 在这种情况下,在使用主题之前,您应该总是尝试找到纯运算符方法。 If you can't then encapsulate the use of a subject in Observable.Create . 如果你不能在Observable.Create封装主题的使用。

It's times like yours that using a subject is fine because you need that external state. 像你这样的时候使用主题很好,因为你需要那个外部状态。

Just pointing out that you code unnessecarily uses Rx. 只是指出你代码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)
}

Here I have removed the Subject and just call directly through to the component s Update method, to illustrate the point. 在这里,我删除了Subject并直接调用componentUpdate方法,以说明这一点。

Perhaps you are looking for a private method of polling? 也许你正在寻找私人投票方法? In which case Observable.Interval could be a good place to start. 在这种情况下, Observable.Interval可能是一个很好的起点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM