如何解决通过回调将await应用于任务
我有一个用于回合游戏的有限状态机。
ProcessState检查当前状态并执行某些操作,然后等待调用回调
private void ProcessState(GameStatus status)
{
switch (status.CurrentState)
{
case State.START_TURN:
DoStartTurn(status,() =>
{
status.SetCurrentState(State.WAIT_MOVE);
});
break;
case State.WAIT_MOVE:
// ...
break;
}
}
“执行”功能发出一个事件,外部使用者最终将调用完成。
private void DoStartTurn(GameStatus status,Action done)
{
OnStartTurn?.Invoke(status,done);
}
是否可以通过异步方法转换ProcessState来简化代码?
我想要以下一些东西
case State.START_TURN:
await DoStartTurn(status);
status.SetCurrentState(State.WAIT_MOVE);
break;
我的问题是将Task概念组合在一起,以将async / await与发出等待回调的发射事件一起使用。
谢谢!
编辑:
我尝试提供整个类(这是Unity的类,但这只是一个普通的类)
using System;
using Sirenix.OdinInspector;
using UnityEngine;
namespace GenericCardEngine
{
[ShowInInspector]
public class Engine
{
[HideInInspector]
public Action<GameStatus,Action> OnInit;
[HideInInspector]
public Action<GameStatus,Action> OnStartGame;
[HideInInspector]
public Action<GameStatus,Action> OnStartTurn;
[HideInInspector]
public Action<GameStatus,Action> OnReshuffle;
[HideInInspector]
public Action<GameStatus,Action<Move>> OnWaitMove;
[HideInInspector]
public Action<GameStatus,Action> OnInvalidMove;
[HideInInspector]
public Action<GameStatus,Action> OnEndTurn;
[HideInInspector]
public Action<GameStatus,Action> OnEndGame;
[ShowInInspector]
GameStatus gameStatus;
public Engine(Config config)
{
gameStatus = new GameStatus(config);
}
public void Begin()
{
gameStatus.OnStateChange += ProcessState;
ProcessState(gameStatus);
}
// Preferisco far ritornare o niente o la mossa dal consumer in modo da poter visualizzare l'intera FSM qui
private void ProcessState(GameStatus status)
{
if (status.ShowDebugLog) Debug.Log($"Processing {status.CurrentState}");
switch (status.CurrentState)
{
case State.INIT:
DoInit(status,() =>
{
status.SetCurrentState(State.START_GAME);
});
break;
case State.START_GAME:
DoStartGame(status,() =>
{
status.SetCurrentState(State.START_TURN);
});
break;
case State.START_TURN:
DoStartTurn(status,() =>
{
if (status.Deck.IsEmpty)
{
status.SetCurrentState(State.RESHUFFLE);
return;
}
status.SetCurrentState(State.WAIT_MOVE);
});
break;
case State.RESHUFFLE:
DoReshuffle(status,() =>
{
status.SetCurrentState(State.WAIT_MOVE);
});
break;
case State.WAIT_MOVE:
DoWaitMove(status,(move) =>
{
MoveType type = Rules.ClassifyMove(status,move);
if (type == MoveType.VALID)
{
Rules.ApplyMove(status,move);
status.SetCurrentState(State.END_TURN);
}
if (type == MoveType.INVALID)
{
status.SetCurrentState(State.INVALID_MOVE);
}
});
break;
case State.INVALID_MOVE:
DoInvalidMove(status,() =>
{
status.SetCurrentState(State.WAIT_MOVE);
});
break;
case State.END_TURN:
DoEndTurn(status,() =>
{
status.SetCurrentState(State.START_TURN);
});
break;
case State.END_GAME:
DoEndGame(status,() =>
{
status.SetCurrentState(State.EXIT);
});
break;
case State.EXIT:
break;
}
}
private void DoInit(GameStatus status,Action done)
{
OnInit?.Invoke(status,done);
}
private void DoStartGame(GameStatus status,Action done)
{
Rules.DealInitialCardsToPlayers(status);
Rules.DealCardsToTable(status);
OnStartGame?.Invoke(status,done);
}
private void DoStartTurn(GameStatus status,Action done)
{
status.Turn++;
Rules.NextPlayer(status);
Rules.DealCardsToPlayerForTurn(status);
OnStartTurn?.Invoke(status,done);
}
private void DoReshuffle(GameStatus status,Action done)
{
Rules.Reshuffle(status);
OnReshuffle?.Invoke(status,done);
}
private void DoWaitMove(GameStatus status,Action<Move> done)
{
OnWaitMove?.Invoke(status,done);
}
private void DoInvalidMove(GameStatus status,Action done)
{
OnInvalidMove(status,done);
}
private void DoEndTurn(GameStatus status,Action done)
{
OnEndTurn?.Invoke(status,done);
}
private void DoEndGame(GameStatus status,Action done)
{
OnEndGame?.Invoke(status,done);
}
}
}
解决方法
您始终可以使用TaskCompletionSource将回调转换为基于任务的调用:
Task DoStartTurn(GameStatus status)
{
var tcs = new TaskCompletionSource<bool>();
OnStartTurn?.Invoke(status,() => tcs.SetResult(true)));
return tcs.Task;
}
但是,如果任务概念一直向下延伸到调用链,则代码将更加优美。在您的示例中,DoStartTurn
是否实际执行异步操作并不明显。或者,如果发生任何异常,该怎么办。或者,如果事件的听众不完全相同,该怎么办。
奇怪的是,您要求Engine客户端提供“对回调的回调”。外观设计可能会发生以下变化:
- 将
Action
签名更改为Func<Task>
,以便您可以正确等待这些签名 - 从这些签名中删除回调(并直接内联调用该逻辑)
类似这样的东西:
class Engine
{
// Func returning Task,so they can be awaited
public Func<GameStatus,Task> OnInit;
public Func<GameStatus,Task> OnStartGame;
// make the ProcessState function async
private async Task ProcessState(GameStatus status)
{
switch (status.CurrentState)
{
case State.INIT:
// business logic for init game
await DoInit(status);
status.SetCurrentState(State.START_GAME);
break;
case State.START_GAME:
await DoStartGame(status);
status.SetCurrentState(State.START_TURN);
break;
// .. etc
}
}
private async Task DoInit(GameStatus status)
{
await OnInit(status);
}
private async Task DoStartGame(GameStatus status)
{
Rules.DealInitialCardsToPlayers(status);
Rules.DealCardsToTable(status);
await OnStartGame(status);
}
// .. etc
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。