0

I'm trying to make a generic 'string to command' method in each of my animal's classes that I can call to make them do things. I can think of a hundred work arounds, but none seem... good. They feel like volatile code.

So I have multiple classes which all inherit from the base class. Each of these subclasses needs to have an 'Interpret()' static method(But you can't/shouldn't do this in C#).

So here's what I wanted to do:

abstract class Action {
}

class Roar : Action {
    public void DoTheRoar() { /* Make the beast roar */ }
}
class Clap : Action {
    public void ClapFinsTogether() { /* Make the animal clap its fins */ }
}
class Jump : Action {
    public void Jump() { /* Epic jump animation */ }
}


abstract class Animal {
    public Action[] actionsToPerform;
    
    public static abstract bool InterpretCommand(string commandFromCircusPerformer, out Action outAction);
}

class Bear : Animal {
    public static override bool InterpretCommand(string commandFromCircusPerformer, out Action outAction){

        //if command is "Roar" then output roar action

        if(commandFromCircusPerformer=="Roar") {
             outAction = new Roar();
        } else {
             outAction = null;
        }

        return outAction != null;
    }
}
class Seal : Animal {
    public static override bool InterpretCommand(string commandFromCircusPerformer, out Action outAction){

        //if command is "Clap" output a clap action

        if(commandFromCircusPerformer == "Jump") {
            outAction = new Jump();
        } else if (commandFromCircusPerformer == "Clap") {
            outAction = new Clap();
        } else {
            outAction = null;
        }

        return outAction != null;
    }
}

Anyone got any ideas on how to accomplish what I'm trying to do correctly? I'd prefer to have a static method that I can refer to from each animal's individual class. Any ideas? I've seen people do use generics to create instances of objects, but that doesn't accomplish what I want(Unless I'm doing it wrong). This is the post i'm refering to: What's the correct alternative to static method inheritance?

--Edit--

I forgot critical information. Each animal has a unique ability or action. The reason I want a static virtual/abstract method is to be able to iterate over every subclass and ask it if it can do this action. For example (In poorly written psuedo code) "Can bears fly?... No...(next)... Can dogs fly?... No... (next)... Hey bird, can you fly?... Yes...[Success, Select Bird and perform Bird Actions]"

After doing some more research and after reading a few replies and comments, I think my code's structure is the problem. I'm ganna try to start from scratch(It's only like 300 lines of code) and see if I can add dictionary of actions or something.

P.S. I want this 'Generic' ability so that I can add creatures in the future by simply creating a subclass of 'Animal' and defining it's custom actions

Mr.P4L
  • 11
  • 3
  • Can you show an example of what you trying to achieve with that? (Obviously you know that you can have `static abstract` in interfaces, but I doubt that is anywhere useful to what you are trying to achieve) – Alexei Levenkov Mar 17 '23 at 20:10
  • First guess after reading your question is to have `Animal` include virtual methods for each of the actions (the base class version does nothing). Then have a Dictionary of string tokens (that represent the actions) and delegates to the actions. Subclasses subscribe to an action by implementing an override of the do-nothing version. Now you can do a simple **O(1)** lookup in the dictionary and invoking the delegate. Only subclasses that have overridden the method will react – Flydog57 Mar 17 '23 at 20:10
  • By the way, `Action` isn't a good name for your class. `System.Action` is a delegate that describes each of your actions (`public delegate void Action();` - https://learn.microsoft.com/en-us/dotnet/api/system.action) – Flydog57 Mar 17 '23 at 20:15
  • This was just an example, I just threw that code together. But I really like your idea of adding virtual methods. The issue though with it is that I'm going to have dozens of subclasses of 'Animal' and dozens, maybe even hundreds, of methods for each 'Animal'. So I need a more versatile means of interpreting the commands – Mr.P4L Mar 19 '23 at 13:54
  • Static abstract is exactly what I want but visual studio says it's not supported and microsoft.com says that that is "Proposed", but not yet implemented. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/static-abstracts-in-interfaces – Mr.P4L Mar 19 '23 at 14:21
  • IMHO create an enum for all possible action names. The enum will then be responsible for parsing strings. Then just `Can(Action action) / Do(Action action)`. If the enum has `[Flags]`, then you can `|` the valid actions into a single variable. – Jeremy Lakeman Mar 21 '23 at 02:58

2 Answers2

1

Welp, I found a simple work around that works for my application. Though the downside is that the compiler doesn't let you know that the method needs to be 'overriden'.

So static abstract and static virtual don't work in C#(as far as I know), but I still want the ability to call any subclass of my parent class and call InterpretCommand() and each subclass be able to individually handle the interpretation. We do this by simply adding the new prefix to the methods.

Something like this:

//The parent class for the actions
public abstract class animalAction {
    public abstract void DoAnimalAction();
}
public class Jump : animalAction {
    public override void DoAnimalAction() {
        /* Activate cool jump animation */
    }
}
public class Roar : animalAction {
    public override void DoAnimalAction() {
        /* Activate roar animation */
    }
}
public class Clap : animalAction {
    public override void DoAnimalAction() {
        /* Do the seals clapping animation */
    }
}

//The parent class for all animals
public class Animal {
    //This is the command that all Animal subclasses will call
    public static animalAction InterpretCommand(string inCommand) {
        throw new System.NotImplementedException();
    }
}
public class Bear : Animal {
    //The 'new' keyword here hides 'Animal.InterpretCommand()'
    new public static animalAction InterpretCommand(string inCommand) {
        if (inCommand == "Roar") {
            return new Roar();
        }
        else {
            return null;
        }
    }
}
public class Seal : Animal {
    //The 'new' keyword here hides 'Animal.InterpretCommand()'
    new public static animalAction InterpretCommand(string inCommand) {
        if(inCommand == "Jump") {
            return new Jump();
        } else if (inCommand == "Clap") {
            return new Clap();
        } else {
            return null;
        }
    }
}

This method works, but later on if I forget to 'override' the InterpretCommand() method on a new animal, the game will throw an error which in a build would result in a crash(I think). Not the perfect solution, but it gets the job done.

Mr.P4L
  • 11
  • 3
0

If I understood you correctly you want abstract statics:

Bear.InterpretCommand("roar", out var a); -> new Roar()
Seal.InterpretCommand("roar", out var a); -> null

Which you can also reinterpret like this:

_inheritanceHolder.GetActionOrDefault<Bear, Roar>();
_inheritanceHolder.GetActionOrDefault<Seal, Roar>();

Which all in all is just a mapper of composite key consisting of two types. Can be put in dictionary/service collection. To stitch them together in compilation time you can mark your classes with interfaces:

ICanRoar {}

or attributes:

[CanRoar]

or make a dictionary (which enables you to change them at runtime):

{(typeof(Bear), typeof(Roar)), ()=> new Roar()},

or register and getting them from IoC framework (which is essentially is created for those kind of things):

services.Register<Action>($"{typeof(Bear)}_{typeof(Roar)}", _ => new Roar());

The point is, you should avoid using static so much. They tend to clutter code with dependency hacks (such as wanting static inheritance, or be more static than static, or add some common functionality in some statics like some precompilation libraries do marking it with attributes, etc). Use them to help generalize code, I mean really common stuff which is more like sugar than actual business logic, but get rid of them when it is not helping.

eocron
  • 6,885
  • 1
  • 21
  • 50
  • I seriously doubt that it is the case - one does not need any "static abstract" inventions to simply call static methods of specific class... But what OP is looking for is still mystery for me. – Alexei Levenkov Mar 17 '23 at 21:03
  • He wants syntax sugar (accessing his "dictionary" of inheritance everywhere) but want to alter that sugar depending on passed type AND also to separate logic between classes. Which is not possible, or shall look like nightmare. – eocron Mar 17 '23 at 21:05
  • I described what I wanted to do poorly and I apologize. I'll edit the question and clarify – Mr.P4L Mar 19 '23 at 13:59