如何安全地覆盖方法

How to override methods safely

我有一个摘要 class 为 child classes 做了很多艰苦的工作。

每个虚拟方法都有一些验证规则。

protected virtual void PrepareToStartGame(TimeSpan remainingTime, GameStarted response)
{
    if (IsGameActive != true)
    {
        Debug.LogError("There isn't an active game!");
        return;
    }

    if (gameStarted)
    {
        Debug.LogError("Game is already started!");
        return;
    }

    if (preparedForStart)
    {
        Debug.LogError("Game is already prepared to start");
        return;
    }

    if (RoomId != response.Game.RoomId)
    {
        Debug.LogError("Wrong GameStarted RoomId!");
        return;
    }
    ...
}

现在,无论何时重写此方法,我都应该能够安全地重写它。例如,这个覆盖有一个错误,因为它不检查验证规则:

protected override void PrepareToStartGame(TimeSpan remainingTime, GameStarted response)
{
    base.PrepareToStartGame(remainingTime, response);

    WaitingForPlayersPanel.gameObject.SetActive(false);
}

我正在考虑解决此问题的方法,它可以帮助我在将来不用担心此类错误。

你对此有何建议?

我知道我可以 return 一个 bool 表示成功或创建一个验证方法来检查每个被覆盖的方法。但我正在寻找更好的解决方案。

在一些有效的情况下,来自服务器的一些事件可能会导致错误的方法调用,所以我不能在这里抛出异常,应该忽略它们。

您可以“反转”依赖关系(基本上实现 template method),使此方法成为非虚拟方法并引入一个新方法:

protected void PrepareToStartGame(TimeSpan remainingTime, GameStarted response)
{
    if (IsGameActive != true)
    {
        Debug.LogError("There isn't an active game!");
        return;
    }

    if (gameStarted)
    {
        Debug.LogError("Game is already started!");
        return;
    }

    if (preparedForStart)
    {
        Debug.LogError("Game is already prepared to start");
        return;
    }

    if (RoomId != response.Game.RoomId)
    {
        Debug.LogError("Wrong GameStarted RoomId!");
        return;
    }
    ...

    PrepareToStartGameInner(remainingTime, response);
}   

protected virtual void PrepareToStartGameInner(remainingTime, response) {};

并且在 child class:

protected override void PrepareToStartGameInner(TimeSpan remainingTime, GameStarted response)
{
    WaitingForPlayersPanel.gameObject.SetActive(false);
}

我会说,如果你想通知验证错误,你应该从方法中抛出自定义错误。调用者应根据工作流要求优雅地处理它。

当您不想将控制权交给继承的方法时,请不要将其声明为虚拟的或可重写的。

当您想给予部分控制权时,引发事件而不是使方法虚拟或可重写。继承的 class 然后可以处理事件而不是覆盖方法。如果他们不需要,他们总是可以选择不处理事件。

例如

    public event EventHandler PreparingToStartGame;
    public event EventHandler PreparedToStartGame;

    protected void OnPrepareToStartGame(TimeSpan remainingTime, GameStarted response)
    {

        PreparingToStartGame?.Invoke(this, new EventArgs());

        if (IsGameActive != true)
        {
            Debug.LogError("There isn't an active game!");
            return;
        }

        if (gameStarted)
        {
            Debug.LogError("Game is already started!");
            return;
        }

        if (preparedForStart)
        {
            Debug.LogError("Game is already prepared to start");
            return;
        }

        if (RoomId != response.Game.RoomId)
        {
            Debug.LogError("Wrong GameStarted RoomId!");
            return;
        }
        //......

        PreparedToStartGame?.Invoke(this, new EventArgs());

    }

我使用 new EventArgs() 只是为了上面的演示。但是你可以创建一个 class 继承自 EventArgs 并使用它来代替,如果你想 give/take 参数值 to/from 方法。

HTH.