简体   繁体   中英

C# Polymorphism - Specify Inherited Type

I've been programming in C# for about six years now and I'm always trying to learn more features this language provides, so I was curious if it was possible to specify an inherited class to achieve a sort of delegate-like behavior, but with entire objects instead of individual methods.

I'm making an RPG game in Unity using C# with a Buff/Debuff system. All the different Buffs/Debuffs inherit from a base Aura class. Character abilities that apply Auras inherit from a base AuraAbility class which in turn inherit from the base Ability class. Here is some relevant code for context.

public abstract class Ability
{
    public string Name;
    public float PowerCoefficient;
    public float CastTime;

    public float Cooldown;
    public float CooldownRemaining;

    public Entity Owner;
    public Entity Target;

    public Ability(Entity owner = null)
    {
        Owner = owner;
    }

    protected virtual void Do()
    {
        CooldownRemaining = Cooldown
    }

    /* Other Methods */
}

public class AuraAbility : Ability
{
    /*
     * This would be whatever the delegate-like object would be
     */
    public Aura SpecifiedAura;

    public AuraAbility(Entity owner = null)
        : base(owner)
    {
        // Overridden constructor functionality
    }

    protected override Do()
    {
        /*
         * This is where I want to construct the specified Aura type
         * This isn't real code but just an example of what I want
         */
        Target.AddAura(new SpecifiedAura(Target, Owner));

        base.Do();
    }
}

public class Attune : AuraAbility
{
    public Attune(Entity owner = null)
        : base(owner)
    {
        Name = "Attune";

        CastTime = 0f;
        Cooldown = 4.0f;

        /*
         * Not real code - but this is where I would specify which inherited aura type I wanted
         */
        SpecifiedAura = AttuneBuff;
    }
}

public abstract class Aura
{
    public Raider Owner;
    public Entity Applier;

    public float Duration;
    protected float DurationRemaining;

    public Aura(Entity owner, Entity applier = null)
    {
        Applier = applier;
        Owner = owner;
    }

    /* Other Methods */
}

public class AttuneBuff : Aura
{
     public AttuneBuff(Entity owner, Entity applier = null)
         : base(owner, applier)
     {
         // Overridden constructor functionality
     }

     /* Overridden Methods */
}

Now I was wondering if there was a way to specify which inherited Aura type I wanted the AuraAbility.Do() to construct and add. Sort of similar to how one would specify a delegate method when defining a callback, I want to specify a "delegate object type" but I'm not sure what the correct terminology is or if this type of functionality even exists.

My fallback solution would be to just override Do() in Attune to construct and add AttuneBuff. I think this is the most straightforward solution but I don't want to do it since it requires unnecessarily overriding Do() and duplicating code.

Another solution would be to make AuraAbility a generic, like public class AuraAbility<T> where T : Aura . And have abilities inherit like public class Attune : AuraAbility<AttuneBuff> . And use Activator to construct the generic type in AuraAbility.Do().

Since I already have solutions I'm mostly asking out of curiosity and for learning's sake, since I'm not sure what this type of functionality is called or if it even exists.

If your only goal is to be able to instanciate an unknown type

public Aura SpecifiedAura;
// ...
Target.AddAura(new SpecifiedAura(Target, Owner));

I could think to replace public Aura SpecifiedAura; by (if Target is of type TTarget and Owner of type TOwner )

public Func<TTarget, TOwner, Aura> SpecifiedAuraFunc;

so that you can do:

Target.AddAura(SpecifiedAuraFunc(Target, Owner));

and you would define it as:

SpecifiedAuraFunc = (target, owner) => new AttuneBuff(target, owner)

If you don't want only to instanciate an unknown type, I have no idea right now

You can do what you want to do.

public abstract class Ability
{
    public Entity Owner;
    public Entity Target;

    public Ability(Entity owner = null)
    {
        Owner = owner;
    }

    protected abstract void Do();

    /* Other Methods */
}

public abstract class AuraAbility : Ability
{
    private readonly Func<Entity, Entity, Aura> auraFactory;

    protected AuraAbility(Entity owner, 
        Func<Entity, Entity, Aura> auraFactory) 
        : base(owner)
    {
        this.auraFactory = auraFactory;
    }

    protected override void Do()
    {
        Target.AddAura(auraFactory(Owner, Target));
    }
}

public class Attune : AuraAbility
{
    public Attune(Entity owner = null)
        : base(owner, (o, target) => new SpecifiedAura(o, target))
    {
    }
}

Please note, some (many) aspects of your program would benefit from significant code review. I suggest a post on the code review stack exchange.

I would make AuraAbility abstract and then have some sort of method that creates the Aura type eg abstract Aura CreateAura(Entity owner, Entity applier = null)

This would be overridden in BuffAura like so:

protected override Aura CreateAura(Entity owner, Entity applier = null) => new BuffAura(owner, applier);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM