是否可以要求派生类实现基类的虚方法?

huangapple go评论75阅读模式
英文:

Is it possible to require a derived class to implement a virtual method from base class?

问题

我想要一个方法,在基类中执行一致的操作,而派生类可以执行不同的操作(因此它是虚拟的),同时要求在派生类中实现它,就像它是抽象的一样。要求它是必需的将防止在我和其他人工作时忘记实现它。

要分解一下(这段代码是为Unity项目编写的):

// 基类
public abstract class Attack : MonoBehaviour
{
    public virtual void AttackFinished()
    {
        Entity.StateController.SwitchToState<IdleState>();
    }
}

// 子类
public class PlayerSwordAttack : Attack
{
    // 我希望这个实现是必需的,而不是可选的
    // 同时仍然执行基类中的操作
    public override void AttackFinished()
    {
        base.AttackFinished();
        // 额外的操作
    }
}

在这种情况下,我希望PlayerSwordAttack或任何其他子类都需要实现AttackFinished(),而不是将它声明为抽象的。

简而言之:实际上,我希望有public abstract virtual void AttackFinished()这样的语法,但据我所知这是不可能的。但我希望能够学到一种方法来实现这个目标。

鉴于我在这里或其他地方没有找到这个问题的解答,我预计答案会是"别傻了,这是不可能的",但我还是想问一下,因为我的一个朋友也有同样的问题。

英文:

I want a method to do a consistent thing in base class while derived classes can have it do something different (hence it being virtual), while requiring it be implemented in derived classes as if it were abstract. It being required would prevent forgetfulness of implementing it among myself and others working on it.

To break it down (this code is for a Unity project):

// Base
public abstract class Attack : MonoBehaviour
{
    public virtual void AttackFinished()
    {
        Entity.StateController.SwitchToState<IdleState>();
    }
}

// Child
public class PlayerSwordAttack : Attack
{
    // I want this implementation to be required, not optional
    // while having it still do the stuff it does in base
    public override void AttackFinished()
    {
        base.AttackFinished();
        // Extra stuff
    }
}

In this case, I want PlayerSwordAttack or any other child classes to require implementing AttackFinished() without it being abstract.

Tl;dr: Essentially I want public abstract virtual void AttackFinished() which isn't possible as far as I know, but I hope to learn of a way to do it.

Given that I haven't found this question posted here or anywhere else, I'm expecting the answer to be "don't be silly, it's impossible" but thought I'd ask, since a friend of mine has had the same question.

答案1

得分: 1

只需将其拆分为两个方法。一个是非虚拟、非抽象的,因此不可重写。这将调用派生类被强制实现的抽象方法。

public abstract class Attack
{
    public void AttackFinished()
    {
        Entity.StateController.SwitchToState<IdleState>();
        OnAttackFinished();
    }
    
    protected abstract void OnAttackFinished();
}

public class PlayerSwordAttack : Attack
{
    protected override void OnAttackFinished()
    {
        // 额外的内容
    }
}

请注意,这类似于在原始代码中强制调用派生类的实现中的 base.AttackFinished。如果您希望派生类可以选择不执行此操作,可以添加一个额外的属性:

public abstract class Attack
{
    public void AttackFinished()
    {
        if (ShouldChangeStateAfterAttack) {
            Entity.StateController.SwitchToState<IdleState>();
        }
        OnAttackFinished();
    }
    
    protected abstract void OnAttackFinished();
    
    // 也可以使用虚拟方法提供默认实现
    protected abstract bool ShouldChangeStateAfterAttack { get; }
}

public class PlayerSwordAttack : Attack
{
    protected override void OnAttackFinished()
    {
        // 额外的内容
    }

    protected override bool ShouldChangeStateAfterAttack => false;
}

在原始代码中,base.AttackFinished 也可以从派生类的任何位置独立调用。如果您也希望如此,只需将 Entity.StateController.SwitchToState<IdleState>(); 提取为另一个方法。

英文:

Just break it down into two methods. One is non-virtual, non-abstract, so is not overridable. This will call the abstract method that derived classes are forced to implement.

public abstract class Attack
{
    public void AttackFinished()
    {
        Entity.StateController.SwitchToState<IdleState>();
        OnAttackFinished();
    }
    
    protected abstract void OnAttackFinished();
}

public class PlayerSwordAttack : Attack
{
    protected override void OnAttackFinished()
    {
        // Extra stuff
    }
}

Note that this is like forcing base.AttackFinished in your original code to be called in the derived classes' implementations. If you want derived classes to opt out of this, you can add an additional property:

public abstract class Attack
{
    public void AttackFinished()
    {
        if (ShouldChangeStateAfterAttack) {
            Entity.StateController.SwitchToState<IdleState>();
        }
        OnAttackFinished();
    }
    
    protected abstract void OnAttackFinished();
    
    // could also be virtual instead to provide a default implementation
    protected abstract bool ShouldChangeStateAfterAttack { get; }
}

public class PlayerSwordAttack : Attack
{
    protected override void OnAttackFinished()
    {
        // Extra stuff
    }

    protected override bool ShouldChangeStateAfterAttack => false;
}

base.AttackFinished in your original code could also be called independently, from anywhere in the derived class. If you also want that, simply extract the line Entity.StateController.SwitchToState<IdleState>(); as another method.

huangapple
  • 本文由 发表于 2023年1月9日 09:25:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75052445.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定