Is it possible to have an override method call its abstract virtual method?

I just got back into C# after working with PHP for about a year. Back when I was doing C# I didn’t have the kind of knowledge that I do now. I would always stay far away from Abstract classes and Interfaces. Since then I have gained enough experience to have a look at it. I am just wondering about something.

Note, the example below is just an idea I had to describe the problem. I am aware that this is probably not the way to go, even if it works. My goal is to have the Abstract class update on its own once Consume is called on one of the derived classes.

Imagine this:

public interface IConsumable
{
    void Consume();
}

public abstract class AbstractConsumable : IConsumable
{
    private bool _consumed = false;

    public virtual void Consume()
    {
        _consumed = true;
    }
}

public class HealthyConsumable: AbstractConsumable
{
    public override void Consume()
    {
        // Do something healthy and ...
        base.Consume(); // Would like to avoid this...
    }
}

public class PoisonousConsumable: AbstractConsumable
{
    public override void Consume()
    {
        // Do something poisonous and ...
        base.Consume(); // Would like to avoid this...
    }
}

EDIT:
It should be fairly obvious from the questions description and the provided example, what I am trying to achieve. Just need a push in the right direction. I am aware this is not directly a Unity Related question, but I thought I would ask here as well as on StackOverflow in case someone here could benefit from the answer.

What I would like to achieve here is not having to call base.Consume() on the override methods, but still have the abstract class set _consumed once the derived classes call their Consume()methods.[/S][/S]

Why have you made AbstractConsumable abstract? (it does not have any abstract members despite the thread title) Do you simply want to ensure it can not be instantiated?

Just in case this is part of the confusion (and it may not be) you don’t have to have an abstract base class to use inheritance.

What assistance are you hoping for? (I don’t think it is obvious sorry)

1 Like

@nat42 - I updated the original question a bit and I got the answer I was looking for on StackOverflow.

public interface IConsumable
{
    void Consume();
}

public abstract class AbstractConsumable : IConsumable
{
    private bool _consumed = false;

    public void Consume()
    {
        _consumed = true;
        ConsumeBehaviour();
    }
 
    protected abstract void ConsumeBehaviour();
}

public class HealthyConsumable: AbstractConsumable
{
    protected override void ConsumeBehaviour()
    {
        // Do something healthy and ...
    }
}

public class PoisonousConsumable: AbstractConsumable
{
    protected override void ConsumeBehaviour()
    {
        // Do something poisonous and ...
    }
}
1 Like

That works great and its clean, but since you mentioned that you are just getting your hands dirty with interfaces and abstract classes I’ll point out a use mistake here.

In general you either want to use either an inheritance or an interface for each behavior. So int this case you should not use the interface since that interface would just be used by AbstractConsumable and it’s inheritance, you want the definition and the base implementation on the same place.

Interfaces are used when the methods implementations would be different for each class that uses it. In this case the base implementation of Consume will always be the same for everyone. So an interface is an overkill.

1 Like

Another way to do this is to use composition instead of inheritance. Have a Consumable that has a list of ConsumptionEffects. Then to build a health potion, you create a consumable and add a heal consumption effect. This is the also basic design philosophy Unity uses with GameObjects, so you might as well embrace it.

This also has the added benefit of not having “rigid” consumables. What if you want a healing potion that buffs your damage but then poisons you after 30 seconds? That’s a total nightmare in your inheritance solution.

2 Likes

Thank you both for your answers.

@whileBreak - I get what you mean. I was thinking about that they seemed a little useless in this context. Thank you for pointing it out.

@GroZZleR - I would love if you could provide an example based on what I posted earlier. Just to make it clear what you are talking about and also if it could help other people stumbling upon this post in the future.

Sure. I just typed this here:

class ConsumableEffect
{
   public abstract void OnEffect(GameObject target);
}

class HealthConsumableEffect
{
   public int quantity;

   public override void OnEffect(GameObject target)
   {
       Health health = target.GetComponent<Health>();

       if(health != null)
       {
           health.current += quantity;
       }
   }
}

class ManaConsumableEffect
{
   public int quantity;

   public override void OnEffect(GameObject target)
   {
       Mana mana = target.GetComponent<Mana>();

       if(mana != null)
       {
           mana.current += quantity;
       }
   }
}

class Consumable
{
   public List<ConsumableEffect> effects;

   public void OnConsume(GameObject target)
   {
       foreach(ConsumableEffect effect in effects)
           effect.OnEffect(target);
   }
}

So with just those two effects, we can make the following potions:

  1. a healing potion
  2. a mana potion
  3. a combination healing and mana potion (Blizzard calls them rejuvenation potion)
  4. a healing potion that damages your mana
  5. a mana potion that damages your health
  6. a potion that hurts both your health and mana (not sure why you’d drink it)

Much better than creating 6 unique derived classes.

1 Like

@GroZZleR i think you missed the inheritance on your health and mana consumable effects.

In the earlier example the interface should be on the consumable behavior not your abstractconsumable.
This would make what it looks like you were trying to do the strategy pattern.
Your different types would implement your strategy interface. You would then instantiate one and assign it to your consumable. Which would use it to do some work.

1 Like

Another way of looking at it is interfaces are used only to fake multiple inheritance. C# copies Java this way, and if you read about them in Java, they explain this. Not being able to implement anything in an interface is just a unfortunate side-effect.

If you suspect you may need to real-inherit from something else, you’d might make Consumable an interface. Maybe you suspect you’ll have lots of Food subtypes, and some will be consumable, and some won’t. But not a big deal, since it’s simple to turn a class into an interface or vice-versa. If you have Consumable C1;, it works the same, regardless.

2 Likes