Search Unity

Need To Stop Execution of Subclass Method when Base is Called

Discussion in 'Scripting' started by MrHappyKiller, Aug 1, 2017.

  1. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    I'm trying to clean up some code for a console I'm designing. What I'd like to do is use the CommandBase class to do basic checking of the input and stop the execution of the subclass method if the checking is true for some certain thing. In this case, it's parameter count for the command:

    CommandBase Method:
    Code (CSharp):
    1.     public void LogCommandError(string errorDefinition)
    2.     {
    3.         UserConsole.AddToActionLog(errorDefinition != null ? "Error: " + errorDefinition : "Unknown Error!");
    4.     }
    5.  
    6.     public virtual void ProcessCommand(string commandName, string[] paramInfo)
    7.     {
    8.         if (paramInfo.Length < commandParams.Length || paramInfo.Length > commandParams.Length)
    9.         {
    10.             Debug.Log("Logging errors");
    11.  
    12.             LogCommandError("Params provided did not match command's!");
    13.  
    14.             return;
    15.         }
    16.     }
    CommandClass Method:
    Code (CSharp):
    1.     public override void ProcessCommand(string commandName, string[] paramInfo)
    2.     {
    3.         Debug.Log(string.Format("paramInfo: {0}, commandParams: {1}", paramInfo.Length, commandParams.Length));
    4.  
    5.         Debug.Log("Called Command");
    6.  
    7.         base.ProcessCommand(commandName, paramInfo);
    8.  
    9.         PlayerManager player;
    10.  
    11.         if (GameObject.FindGameObjectWithTag("Player") != null)
    12.         {
    13.             player = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerManager>();
    14.         }
    15.         else
    16.         {
    17.             LogCommandError("Can't find Player in level!");
    18.  
    19.             return;
    20.         }
    21.  
    22.         float result;
    23.  
    24.         float.TryParse(paramInfo[0], out result);
    25.  
    26.         player.DamageEntity(result);
    27.     }
    I'm guessing this is because I'm overriding the method. But if I don't, it only calls the base method. :/
     
  2. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Side note: Excuse my somewhat lack of knowledge in class extension. For some reason, I haven't been able to fully grasp it correctly.
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Maybe you're not really wanting to do inheritance here. Maybe you're looking more for the component approach? You could have the base class make decisions about how to dispatch the command to other plugged-in components, if they are present.

    That way there is no necessary binding between then. You can even use something like interfaces to discover components that you can call with varying classes of command.

    As an aside, the Unity GameObject and Component model is a fine example of has-a (component) architecture rather than is-a (inheritance) architecture.
     
    Ryiah likes this.
  4. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    All of my instincts are saying you don't want to do this. As a general rule a method should not be aware of its calling context, which includes the ability to terminate the calling method. It would be preferable to simply have a different method that returns a bool if the message is valid. This could even be on the base class if you are stuck on inheritance.

    However if you do want to do this, the only sure fire way would be to throw and invalid parameter error. Then you can catch it or not in your calling code as you please.
     
    Ryiah likes this.
  5. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Kurt-Dekker has already provided a better long term design but, in the spirit of answering your original question, here is how I would structure the code if I was forced to use inheritance.

    There's a number of other improvements that could be made but I choose to leave your code mostly as-is which will hopefully help you understand the main structural change which solves your issue.

    Code (CSharp):
    1.   public void LogCommandError(string errorDefinition)
    2.   {
    3.     UserConsole.AddToActionLog(errorDefinition != null ? "Error: " + errorDefinition : "Unknown Error!");
    4.   }
    5.  
    6.   public void ProcessCommand(string commandName, string[] paramInfo)
    7.   {
    8.     if (IsValidParameters(commandName, paramInfo))
    9.       ExecuteCommand(commandName, paramInfo);
    10.     else
    11.     {
    12.       Debug.Log("Logging errors");
    13.       LogCommandError("Params provided did not match command's!");
    14.     }
    15.   }
    16.  
    17.   private bool IsValidParameters(string commandName, string[] paramInfo)
    18.   {
    19.     return paramInfo.Length == commandParams.Length;
    20.   }
    21.  
    22.   protected void abstract ExecuteCommand(string commandName, string[] paramInfo);
    CommandClass Method:
    Code (CSharp):
    1.   protected override void ExecuteCommand(string commandName, string[] paramInfo)
    2.   {
    3.     Debug.Log(string.Format("paramInfo: {0}, commandParams: {1}", paramInfo.Length, commandParams.Length));
    4.     Debug.Log("Called Command");
    5.  
    6.     PlayerManager player;
    7.  
    8.     if (GameObject.FindGameObjectWithTag("Player") != null)
    9.     {
    10.       player = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerManager>();
    11.     }
    12.     else
    13.     {
    14.       LogCommandError("Can't find Player in level!");
    15.       return;
    16.     }
    17.  
    18.     float result;
    19.     float.TryParse(paramInfo[0], out result);
    20.     player.DamageEntity(result);
    21.   }
     
    Last edited: Aug 1, 2017
    Ryiah likes this.
  6. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Hm, you make a very good point. Seeing as my commands would always be available, I should use an empty gameobject to store my command scripts that implement the ICommand interface then use the getcomponant of type ICommand in order to execute?

    Make a lot more sense the way you put it. I was thinking about using a bool to do as you said. I guess I'm too caught up in making my code as clean and as easy to use as possible (even though I don't plan to release my code as an asset) I'll work with what you said. :)

    Ah, see I never truly understood the use of the abstract keyword. I'll have to look into it more, but isn't it mostly like how an interface is? The structure of a method for a subclass?
     
  7. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    abstract has different meanings depending where it is used. When applied to methods, thinking of it like an interface is actually a pretty good analogy, though I think you might be missing something subtle caused by the overloading of the word interface.

    C# confuses this matter because the language defines a keyword interface and applies certain rules to types created with it. Historically speaking however, an interface is simply a set of messages that a class knows how to respond to. In C#, we call these messages method calls and we say that a class implements those methods. What's important to understand is that even if you don't use the C# keyword interface, every class you make defines an interface by the set of public methods on it.

    When you extend a base class through class inheritance, you are implicitly agreeing to support the interface declared by the base class. The difference between inheriting from a class and an interface is that a base class can also provide data and code to implement the interface, while a C# interface is just a set of methods which need to implemented. When you use the keyword abstract on a method inside of an abstract class, you are basically opting out of providing the data and code so you are getting behavior very similar to a C# interface.

    I scrounged up these old posts of mine because they talk more extensively on class vs interface inheritance than I'm willing to get into now. It's a long read and not all applicable, so take it or leave it. Hope it helps!

    https://forum.unity3d.com/threads/interfaces-vs-inheritance-help.373237/#post-2424838
    https://forum.unity3d.com/threads/interfaces-vs-inheritance-help.373237/#post-2426432
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Abstract and interface and virtual and override and all that sorta good CIS type stuff is awesome once you have an idea of what your problem space is, as they can be valuable tools to implement your engineering solution elegantly.

    HOWEVER, if you are just pottering around with user interfaces and game design in Unity3D, agonizing about the right inheritance or component or abstraction or what ever is only going to slow you down. Honest.

    It's far more important to quickly write the code, get it running on target hardware, use it, give it to some sample users, make iterations, do more testing, and ultimately see how it fits into the future plans of your game (does it scale, etc.), and then, ONLY then, iterate and refactor it into subclasses or subcomponents or whatever your favorite CIS word of the day is to make it better.

    Until then, fiddling with language constructs you don't need or don't fully understand the implications of is only going to derail your project. I've seen it happen time and time again, and in fact I saw it happen to another team just in the past week. Stay on target and solve your primary game design and implementation FIRST, then get fancy-schmancy with your leet haxors code skeelz once you have it proven out.

    STAY ON TARGET...
     
    Last edited: Aug 2, 2017
    BlackPete, eisenpony and Kiwasi like this.
  9. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Sage advice. I apologize if I'm tempting anyone down the rabbit hole.

    I lean heavily towards "pottering-around-to-learn" when spending time in Unity Land, so I tend to have very different guiding principles than someone who is trying to make a game.
     
  10. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    eisenpony - Thanks a lot! I've honestly had just a tad bit of confusion with generics in general after coming from an intermediate knowledge of Java. I will be reading over your explanations tomorrow so thank you again. :)

    Kurt - I completely agree with you. I've been trying to stop myself from finding the most efficient way of writing a piece of code so I can move on a bit. With me though, I am taking my current project in small steps. I am achieving a small goal (player movement, save/load manager, console, etc.) and trying to figure out different things I may not have known in order to broaden my mind. This is with the ultimate goal of not only releasing my project, but also coming from it with a wide understanding of Unity and C# logic I once couldn't grasp And it really has been helping. :) If I was on a stern time crunch, I would definitely make some sloppy code and rework it later (although, I hate keeping a bunch of logic in the Update method and such so I normally make sub methods), Thank you for your input and thank you for your explanation. :)