当您发现自己需要按特定顺序执行事件时的 C# 设计模式

C# design pattern when you find yourself in need of events to be executed in specific order

我正在用 C# 制作一个游戏,当玩家移动时,两个不同的 class 会通过事件得到通知。

其中一个 class 是 RenderGrid,一旦玩家移动,RenderGrid 将仅实例化现在在屏幕上可见的新游戏图块。它还包含两个向量,描述当前正在渲染的网格的左下角和右上角

另一个是 WorldManager class,一旦玩家移动,它就会检查是否需要加载和创建游戏世界的新区块。为此,它需要检查 RenderGrid 的角以确保它们仍在已加载块的边界内。

这就是问题所在,因为 WorldManager 取决于 事件首先在 RenderGrid 上处理,然后在 WorldManager 上处理,因此中断事件模式

在伪代码中,这里是 RenderGrid:

public class RenderGrid 
{

    public Vector2 bottomLeft;
    public Vector2 topRight;

    public RenderGrid() 
    {
        Player.onPlayerMoved += playerMoved;
    }

    ~RenderGrid() 
    {
        Player.onPlayerMoved -= playerMoved;
    }

    private void playerMoved(Vector2 delta, Vector2 position) 
    {
        // updates the bottomLeft and topRight corners
    }
}

和 WorldGrid:

public class WorldGrid 
{
    public WorldGrid() 
    {
        Player.onPlayerMoved += playerMoved;
    }

    ~WorldGrid() 
    {
        Player.onPlayerMoved -= playerMoved;
    }

    private void playerMoved(Vector2 delta, Vector2 position) 
    {
        // it needs the corners of the renderGrid, but since those are also updated when player moves, we can't be sure
        // wheter they've been updated here or not
    }
}

使用单独的事件通知 RenderGrid 角落已更新,听着好像是意大利面,我不确定如何从这里开始

除了 SO 上关于事件顺序是否得到保证的传奇争议:

问问自己,谁对事件的响应责任最大,那么这个应该先响应。在此基础上,在 WorldGrid 和 RenderGrid 之间注册另一个事件(具有不同的名称)有什么问题?

如果 RenderGrid 归 WorldGrid 所有,反之亦然,您甚至可以定义并调用 RenderGrid.NotifyContentLoaded 或 WorldGrid.NotifyBordersChanged.

链接事件

似乎 RenderGrid 应该处理指示玩家移动的事件。 RenderGrid 然后可以发布自己的事件。 WorldGrid 会订阅这些事件,因此它只会在 Rendergrid 完成处理后触发。

典型的模式是设置自定义事件参数、委托和处理程序,如下所示:

自定义事件参数,处理程序委托:

public class PlayerMovedEventArgs
{
    public Vector2 Delta { get; set; }
    public Vector2 Position { get; set; }
}

public delegate void PlayerMovedHandler(object sender, PlayerMovedEventArgs e);

然后是渲染网格:

public class RenderGrid 
{
    public Vector2 bottomLeft;
    public Vector2 topRight;
    public Player _player;

    public event PlayerMovedHandler Changed;

    public RenderGrid(Player player) 
    {
        _player = player;
        _player.OnPlayerMoved += PlayerMovedInGrid;
    }

    private void UpdateCorners(Vector2 delta, Vector2 position)
    {
        //ToDo: Update corners
    }
    private void PlayerMovedInGrid(object sender, PlayerMovedEventArgs e) 
    {
        UpdateCorners(e.Delta, e.Position);
        OnChanged(e);
    }

    protected void OnChanged(PlayerMovedEventArgs e)
    {
        if (Changed != null) Changed(this, e);
    }
}

最后是 WorldGrid。这里的重要变化是 WorldGrid 订阅了 RenderGrid 的事件(不是 Player 的)。

public class WorldGrid 
{
    private readonly Player _player;
    private readonly RenderGrid _renderGrid;

    public WorldGrid(Player player, RenderGrid renderGrid) 
    {
        _player = player;
        _renderGrid = renderGrid;
        _renderGrid.Changed += this.PlayerMovedInWorld;
    }

    private void PlayerMovedInWorld(object sender, PlayerMovedEventArgs e) 
    {
        // it needs the corners of the renderGrid, but since those are also updated when player moves, we can't be sure
        // wheter they've been updated here or not
    }
}