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.
CodePudding user response:
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.