Search Unity

Need help with Inheritance in C#

Discussion in 'Scripting' started by 7331Squall, Jan 5, 2016.

  1. 7331Squall

    7331Squall

    Joined:
    Jan 5, 2016
    Posts:
    4
    I'm having a few problems with inheritance in C#, and Google hasn't been a kind master to me. It's actually very simple. I have two classes:

    BaseClass:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. [System.Serializable]
    4. public class BasicClass {
    5.     const string className = "BASE CLASS";
    6.     public string getClassName () {
    7.         return className;
    8.     }
    9. }
    and Dog:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. [System.Serializable]
    4. public class Dog : BasicClass {
    5.     const string className = "DOG";
    6. }
    Then I create an Array like this:
    Code (CSharp):
    1. private void InitializeClassManager ( ) {
    2.     classManager = new BasicClass[1];
    3.     classManager[0] = new Dog();
    4. }
    And I proceed to populate my HUD list like this:
    Code (CSharp):
    1. GameObject newClass = (GameObject)Resources.Load("UI_Clicker_Class");
    2. foreach (ClickerClass c in controller.classManager) { //this controller.classManager is the one I've created before.
    3.     GameObject spawned = Instantiate(newClass);
    4.     GameObject child = spawned.transform.Find("ClassName").gameObject;
    5.     child.GetComponent<Text>().text = c.getClassName();
    6. }
    I expected the content displayed to be "DOG", as it should've pulled the constant from the Dog class, however, it displays "BASE CLASS", indicating the constant comes from the parent class, the BaseClass.

    What do I need to do on these classes to make sure Dog's getClassName() gets that class' constant?

    Do I need to redeclare the method? Wouldn't that break the purpose of inheriting the class, as the child method would be exactly the same as the parent one?
     
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    "virtual" in the base class, "override" in the subclass

    https://msdn.microsoft.com/en-us/library/9fkccyh4.aspx


    might also want to look at the accessors, you're trying to achieve something similar with a method by the looks of it..
    https://msdn.microsoft.com/en-gb/library/aa287786(v=vs.71).aspx

    hmmm... actually the more I think about it...

    the name field in the base class is private (by default) as is the child's, since the child class doesn't implement it's own version of the function being called the parent's method can only return the parent's field value (since it's the only field by that name within the function's scope)...

    if you want the child class to simply inherit and overwrite the field, you're probably better off setting it to protected/public and just updating it in the child class.
     
    Last edited: Jan 5, 2016
  3. 7331Squall

    7331Squall

    Joined:
    Jan 5, 2016
    Posts:
    4
    But, isn't there a way to call the method without rewriting it in the subclass, and returning the correct data?
     
  4. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    sorry, I edited my post a fair bit... end of day posting ftw :)

    at the moment I think you've got two private "name" fields, the parent "name" field isn't being inherited because it's private.
    If you set it to protected instead of private you'll get the "child hides parent field" warning because it's being redeclared in the child. If you set it to protected in the parent, and simple do "name = "dog";" you'll get an inherited member which updates the single name field in the parent/child class and the effect you're after (I think)
     
  5. 7331Squall

    7331Squall

    Joined:
    Jan 5, 2016
    Posts:
    4
    I've tried to do all of that, I still got no success.

    I mean, I just wanted to call "Dog.getClassName()" and have it return "DOG".

    I've tried this in an external C# compiler, but the results are the same.

    I've decided to just rewrite the methods the exact same way in the children classes... Now it works, but the amount of unnecessary copy-pasting because C# does not have a decent inheritance baffles me... X_X

    So the BaseClass is now like this:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. [System.Serializable]
    4. public class BasicClass {
    5.     const string className = "BASE CLASS";
    6.     virtual public string getClassName () {
    7.         return className;
    8.     }
    9. }
    And the Dog class is now like this:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. [System.Serializable]
    4. public class Dog : BasicClass {
    5.     const string className = "DOG";
    6.     //Redefining the next method should not be necessary...
    7.     override public string getClassName () {
    8.         return className;
    9.     }
    10. }
    This works, but... Makes no sense to me defining the children methods ALL OVER AGAIN for EVERY child.
     
  6. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    I believe that when you do it that way, BasicClass's className still exists, it's just hidden. If you were to output "base.className" in Dog.getClassName(), I believe it would spit out "BASE CLASS". The reason you must override getClassName in that scenario is because even on a Dog, BasicClass.getClassName() can only see BasicClass.className - it has no concept of its child class's members.

    FWIW, I have never seen a member overridden in a derived class in the real world. Probably for this exact reason - it causes confusion and ambiguity. Override methods and properties till the cows come home, but members? Asking for trouble.

    This is absolutely a case where hard-coding a string into a property is a-okay:
    Code (csharp):
    1. public class BasicClass : MonoBehaviour {
    2. public virtual string className { get { return "BASE CLASS"; } }
    3. }
    4.  
    5. ...
    6.  
    7. public class Dog : BasicClass {
    8. public override string className { get { return "DOG"; } }
    9. }
    This actually works exactly like you'd expect.
     
    Munchy2007 likes this.
  7. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    Starmanta is correct - fields are not overridden, for good reason. Overriding a property get method is what you want to do. You also have the option of assigning the field in the constructor of the child class, and simply using the base class method. I tend to go with properties though.
     
    Munchy2007 likes this.
  8. 7331Squall

    7331Squall

    Joined:
    Jan 5, 2016
    Posts:
    4
    What? Fields are not overridden??? That's the first time I'm hearing this!

    I mean, I have another method called "GetMPS" that returns how much money that class will generate per second.

    As stated before, If I call this method from the DOG, he will return the MPS from the BASE. I need to redeclare.... I mean, copy/paste THE WHOLE method, simply because C# is unable to understand that, if I'm not asking for the "Super.value" (or whatever the equivalent should be), he shouldn't return the parent's value!

    And it's somewhat complex, because it involves grabbing at least 5 variables from the class...

    So, If I declare the fields that way, C# will understand what I want to do?
     
  9. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    No need to do a bunch of copy pasting. Fields cannot be overridden, but properties and methods can. What you have done is made two separate variables in different scopes with the same name, and attempting to distinguish between them. C# is functioning as designed, because you are specifically asking for BaseClass.className, even though BaseClass happens to be a Dog. There are easy ways to do what you are asking.

    The first of which is to use hardcoded properties - StarMantra gave you a good example of how this is done, and its no more code than you already tried to write above (aside from the 'get' keyword)

    If you want to still use fields, try setting the variable in the constructor rather than defining a completely new variable in the child class.

    *Disclaimer - typing directly into the browser, can't promise perfect code*

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. [System.Serializable]
    5. public class BasicClass {
    6.  
    7.    protected string className = "BASE CLASS"; //protected lets my child classes see this
    8.  
    9.    //base constructor
    10.    public BasicClass(){}
    11.  
    12.    public string getClassName () {
    13.        return className;
    14.    }
    15. }
    then

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. [System.Serializable]
    5. public class Dog : BasicClass {
    6.    //do not do this, as you are just creating a new privately scoped variable
    7.    // with zero relation to the base class
    8.    //const string className = "DOG";
    9.  
    10.    //instead do this, where you define the base class
    11.     public void Dog() {
    12.         className = "DOG";
    13.     }
    14. }

    You can do that with any number of variables that need to have a different default value than the base class, and you only need to write the getClassName method once, in the base class.

    For your GetMPS method, if your LOGIC changes, you need to override it in the child class.
    Code (csharp):
    1.  
    2. public class BasicClass {
    3.  
    4.    protected string className = "BASE CLASS";
    5.    protected int A=1;
    6.    protected int B=2;
    7.    protected int C=3;
    8.    
    9.    //base constructor
    10.    public BasicClass(){}
    11.  
    12.    public string getClassName () {
    13.        return className;
    14.    }
    15.     //the default method
    16.    public virtual int GetMPS(){
    17.         return A*B + C; //we will return 1*2 + 3, or 5
    18.    }
    19.  
    20.    }
    For a Dog, I choose to use the same logic as the base class, so I dont need to write any extra code at all.


    Code (csharp):
    1.  
    2. public class Dog: BasicClass {
    3.  
    4.     public void Dog() {
    5.         className = "DOG";
    6.         A=2;
    7.         C=4:
    8.     }
    9.  
    10.    //Dogs get MPS the same as base class, but their values are different.
    11.    //we write no additional code, but return 2*2 + 4 = 8 for MPS based on Dog's values for A, B, and C
    12.  
    13. }
    But if I want Cat to change how it calculates getMPS, I simply override the method


    Code (csharp):
    1.  
    2. public class Cat: BasicClass {
    3.  
    4.    private float D= 2;
    5.  
    6.     public void Cat() {
    7.         className = "CAT";
    8.         A=2;
    9.         C=1:
    10.     }
    11.    
    12.    //Cats determine MPS via different logic, so we override the logic here.
    13.  
    14.   public virtual int GetMPS(){
    15.         return A*B + C -D; //we will return 2*2 + 1 - 2, or 1
    16.    }
    17.  
    18. }
    This is a rudimentary approach, and depending on the design of the system I would probably set things up differently (more use of interfaces and properties), but this should do just fine for now.