Как мне превратить систему опроса в 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
и просто позвоните прямо на component
s Update
метод, чтобы проиллюстрировать суть.
Возможно, вы ищете частный метод опроса? В таком случае Observable.Interval
может быть хорошим местом для начала.