How to pass a polymorphic object as a derived one?

to keep things simple I have two classes: ActionTaken and MovementTaken. MovementTaken is a derived class from ActionTaken. I have a Queue full of actions, and each action has a char that determines the type of action. (Every action has a correct type) I want to pass elements of the Queue to a function, that works specifically with a MovementTaken parameter, but since I'm using polymorphism the parameter is of type ActionTaken, but then I cannot use member variables from MovementTaken, but don't exist in ActionTaken. But if I set the function activateMovement's parameter to type MovementTaken, I believe there would be an error, saying you cannot convert a base type to a derived type. Here's the code:

public abstract class ActionTaken : MonoBehaviour
{
    public char type;
    public Transform minionTakingAction;
}

public class MovementTaken : ActionTaken
{
    public int targetTileH;
    public int targetTileV;
    public MovementTaken(Transform _minionTakingAction, int _targetTileH, int _targetTileV)
    {
        type = 'M';
        minionTakingAction = _minionTakingAction;
        targetTileH = _targetTileH;
        targetTileV = _targetTileV;
    }
}

Queue<ActionTaken> actionTaken;
public void activateMovement(ActionTaken toActivate)
{//some code using toActivate's members targetTileH and targetTileV}

Upvotes: 2

Views: 981

Answers (2)

Parrish Husband
Parrish Husband

Reputation: 3178

The advantage of Abstract classes is defining base implementation, or to force derived types into implementation details:

public abstract class ActionTaken : MonoBehaviour
{
    public char Type { get; protected set; }
    public Transform Target { get; }

    // base ctor
    protected ActionTaken(Transform target)
    {
        Type = '\0'; 
        Target = target;
    }

    // Force implementation onto sub class
    public abstract void Activate();
}

public class MovementTaken : ActionTaken
{
    public int TileH { get; set; }
    public int TileV { get; set; }

    public MovementTaken(Transform target, int tileH, int tileV)
        : base(target)
    {
        Type = 'M';
        TileH = tileH;
        TileV = tileV;
    }

    public override void Activate()
    {
        //some code using TileH and TileV
    }
}

Therefore your calling code would be:

Queue<ActionTaken> actionTaken;
public void activateMovement(ActionTaken action)
{
    action.Activate();
}

I'm also not sure what Type is being used for, but if you still need it, it might be better off as a constant defined in each class that derives from ActionTaken if you have more.

This can make sense if you end up filling your Queue<ActionTaken> with various derived movement types. Otherwise your ActivateMovement method could end up being a long switch statement.

An interface also might be advantageous here:

public interface IActionTaken
{
    Transform Target { get; }

    void Activate();
}

Which you would then replace your queue: Queue<IActionTaken> Actions

The code for invoking all of the actions in the queue could then be extremely straightforward:

while(Actions.Count > 0)
{
    IActionTaken current = Actions.Dequeue();
    current.Activate();
}

Upvotes: 1

Mureinik
Mureinik

Reputation: 311428

If you know the argument passed to the method is a MovementTaken instance, you can just downcast it:

public void activateMovement(ActionTaken toActivate)
{
    MovementTaken casted = toActivate as MovementTaken;
    // Do something with casted.targetTileH and/or caster.targetTileV

Upvotes: 1

Related Questions