如何将轮询系统变成 Rx.Net IObservable?
How do I turn a polling system into an 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
?
你在这里面临的关键问题是你需要一个 observable 的来源。通常,您可以从事件、委托、任务、许多可观察扩展(如 .Interval
或 .Generate
)和主题等各种来源创建可观察对象。
在你的情况下,你必须有一个源,你可以在你的可观察对象之外拥有代码,将值推送到。在这种情况下,主题非常好,但您也可以使用委托。
如果您使用主题,那么您的代码就可以了。唯一的缺点是您可以调用 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
- 您不能 "accidentally" 结束序列。
现在使用主题与 Observable.Create
的整个问题是状态之一。在你的代码中你需要状态,所以一个主题是可以的。不过,一般来说,只要有可能,封装状态是明智的——这就是 Observable.Create
为你所做的。
举个例子:
var i = -1;
var query =
Observable
.Range(0, 10).Select(x =>
{
i = -i * 2;
return x * i;
});
如果我订阅这个 observable 两次,我会得到这两个序列:
(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
可能是一个很好的起点。
我有一款游戏(基于 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
?
你在这里面临的关键问题是你需要一个 observable 的来源。通常,您可以从事件、委托、任务、许多可观察扩展(如 .Interval
或 .Generate
)和主题等各种来源创建可观察对象。
在你的情况下,你必须有一个源,你可以在你的可观察对象之外拥有代码,将值推送到。在这种情况下,主题非常好,但您也可以使用委托。
如果您使用主题,那么您的代码就可以了。唯一的缺点是您可以调用 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
- 您不能 "accidentally" 结束序列。
现在使用主题与 Observable.Create
的整个问题是状态之一。在你的代码中你需要状态,所以一个主题是可以的。不过,一般来说,只要有可能,封装状态是明智的——这就是 Observable.Create
为你所做的。
举个例子:
var i = -1;
var query =
Observable
.Range(0, 10).Select(x =>
{
i = -i * 2;
return x * i;
});
如果我订阅这个 observable 两次,我会得到这两个序列:
(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
可能是一个很好的起点。