Как мне превратить систему опроса в Rx.Net IObservable?

У меня есть игра (на основе 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 ответа

Решение

Ключевой вопрос, с которым вы здесь сталкиваетесь, заключается в том, что вам нужен источник для вашего наблюдаемого. В общем, вы можете создавать наблюдаемые из различных источников из событий, делегатов, задач, многих наблюдаемых расширений (например, .Interval или же .Generate) и предметы.

В вашем случае у вас должен быть источник, к которому у вас может быть код, внешний по отношению к вашему наблюдаемому, push-значениям. В этом случае тема отлично подходит, но вы также можете просто использовать делегата.

Если вы используете тему, то ваш код в порядке. Единственным недостатком является то, что вы могли бы позвонить 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 
-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,

В такие времена, как у вас, использование предмета хорошо, потому что вам нужно это внешнее состояние.

Просто указываю на то, что вы код несуразно используете 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 и просто позвоните прямо на components Update метод, чтобы проиллюстрировать суть.

Возможно, вы ищете частный метод опроса? В таком случае Observable.Interval может быть хорошим местом для начала.

Другие вопросы по тегам