C# 我应该如何在FRP(Rx.Net)中建模循环依赖关系?
我试图通过使用Rx.Net实现Tic-Tac-Toe来学习更多关于函数式反应式编程的知识。我面临的问题是,在我的游戏逻辑中似乎存在循环依赖C# 我应该如何在FRP(Rx.Net)中建模循环依赖关系?,c#,system.reactive,reactive-programming,circular-dependency,rx.net,C#,System.reactive,Reactive Programming,Circular Dependency,Rx.net,我试图通过使用Rx.Net实现Tic-Tac-Toe来学习更多关于函数式反应式编程的知识。我面临的问题是,在我的游戏逻辑中似乎存在循环依赖 命令流(PlaceToken,ResetGame等)是从用户输入流生成的 游戏的当前状态(boardStates)是通过将命令应用于前一个状态,从初始状态开始计算得出的: var initialBoardState = new BoardState(); var boardStates = commands .Scan(initialBoardSt
命令流(PlaceToken
,ResetGame
等)是从用户输入流生成的
游戏的当前状态(boardStates
)是通过将命令
应用于前一个状态,从初始状态开始计算得出的:
var initialBoardState = new BoardState();
var boardStates = commands
.Scan(initialBoardState, (boardState, command) => command.Apply(boardState))
.DistinctUntilChanged();
但是,命令
流应取决于boardStates
流。这是因为有效的命令集随当前状态而变化
例如,PlaceToken
命令只应在用户单击空磁贴时发出,但空磁贴集由当前状态定义
总之,我有两条流,它们似乎相互依赖。在函数式反应式编程中,我应该如何解决这一问题?并非所有事情都需要成为事件。记住,Rx/回调是一种允许您依赖的东西给您回电话(而不依赖您)的方式
作为经验法则
循环依赖关系表示存在设计缺陷
发送命令,接收事件
2) 也可以翻译成
只需调用依赖项上的方法来更改它们的状态,但订阅它们的事件来查看它们的更改
因此,不要将命令视为一个流,而应该将用户操作视为一个流,当听到该流时,可能会创建一个命令
所以董事会成员可能看起来像这样
public class BoardState
{
public void PlaceToken(PlaceTokenCommand placeToken)
{
//Process, then raise event
}
public void Reset()
{
//Process, then raise event
}
public IObservable<?> StateUpdates()
{
}
}
public class TicTacToeViewModel
{
private readonly BoardState _board;
public TicTacToeViewModel()
{
_board = new BoardState();
MoveTokenCommand = new DelegateCommand(MoveToken, CanMoveToken);
ResetBoardCommand = new DelegateCommand(_board.Reset);
board.StateUpdates(state => UpdatePresentation(state));
}
public DelegateCommand MoveTokenCommand { get; private set;}
public DelegateCommand ResetBoardCommand { get; private set;}
private void MoveToken()
{
var token = CurrentToken;
var location = ActiveLocation;
var cmd = new PlaceTokenCommand(token, location);
_board.PlaceToken(cmd);
}
private bool CanMoveToken()
{
//?
}
}
但是作为@Enigmativity,评论中的请求,如果没有MCVE,很难提供合理的帮助
最后一点注意,虽然Rx是功能性的,而且是反应性的,但狂热者会反对将Rx视为FRP(参见Conal,行为等)。虽然@LeeCampbell的解决方案确实有效,但它需要使您的核心模型类可变。相反,我发现最好复制cycle.js采用的方法。你可以看到他们的解释
问题是我们有一个循环。行动流取决于董事会状态流,而董事会状态流又取决于行动流:
boardStream = f(actionStream)
actionStream = g(boardStream)
cycle.js解决方案是使用代理流将所有内容连接在一起:
// Create a proxy
proxyActionStream = new Stream()
// Create our mutually dependent streams using the proxy
boardStream = f(proxyActionStream)
actionStream = g(boardStream)
// Feed actionStream back into proxyActionStream
actionStream.Subscribe(x => proxyActionStream.OnNext(x))
在Rx.NETLand中,代理流应该是ReplaySubject
你唯一需要注意的地方就是失控的反馈循环:如果你的流从未稳定下来,那么它们就会撞上一个无限循环!将流视为相互递归是很有帮助的 你真的需要一份工作来做这件事。