Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Multiple Inheritance / Implementation alternative

Discussion in 'Scripting' started by Stephan-B, Nov 13, 2015.

  1. Stephan-B

    Stephan-B

    Unity Technologies

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    Assuming Multiple Inheritance was supported in C#, I would have the following classes.

    Code (csharp):
    1.  
    2. class BaseClass : MonoBehaviour
    3. {
    4.      // Common functionality
    5. }
    6.  
    7. class SubClassA : BaseClass
    8. {
    9.      // Unique functionality & implementation
    10. }
    11.  
    12. class SubClassB : BaseClass, MaskableGraphic
    13. {
    14.      // Unique functionality & implementation
    15. }
    16.  
    Given that Multiple Inheritance is not supported, I need to have the BaseClass inherit from MaskableGraphic which comes with extra luggage which isn't needs by the SubClassA and more importantly adds extra components via [RequireComponent].

    Any suggestions on how to implement this?
     
    hopetolive likes this.
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,744
    Polymorphik likes this.
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    In C#, like Java, you use interfaces.

    Code (csharp):
    1.  
    2. public interface IBaseType
    3. {
    4.  
    5.     string SomeProperty { get; set; }
    6.  
    7.     void Foo();
    8.  
    9. }
    10.  
    11. public class SubTypeA : MonoBehaviour, IBaseType
    12. {
    13.  
    14.     private string _value;
    15.  
    16.     public string SomeProperty
    17.     {
    18.         get { return _value; }
    19.         set { _value = value; }
    20.     }
    21.  
    22.     public void Foo()
    23.     {
    24.         Debug.Log("DID FOO!");
    25.     }
    26.  
    27. }
    28.  
    29. public class SubTypeB : MaskableGraphic, IBaseType
    30. {
    31.  
    32.     private string _value;
    33.  
    34.     public string SomeProperty
    35.     {
    36.         get { return _value; }
    37.         set { _value = value; }
    38.     }
    39.  
    40.     public void Foo()
    41.     {
    42.         Debug.Log("DID FOO!");
    43.     }
    44.  
    45. }
    46.  
    Thing is interfaces don't allow for generalized implementation though, it only accepts interface definitions.

    What you can do is create consumable composite objects for the interface type if you need generalized implementation.

    Code (csharp):
    1.  
    2. public interface IBaseType
    3. {
    4.  
    5.     string SomeProperty { get; set; }
    6.  
    7.     void Foo();
    8.  
    9. }
    10.  
    11. public class ConsumableBaseType : IBaseType
    12. {
    13.  
    14.     private string _value;
    15.  
    16.     public string SomeProperty
    17.     {
    18.         get { return _value; }
    19.         set { _value = value; }
    20.     }
    21.  
    22.     public void Foo()
    23.     {
    24.         Debug.Log("DID FOO!");
    25.     }
    26.  
    27. }
    28.  
    29. public class SubTypeA : MonoBehaviour, IBaseType
    30. {
    31.  
    32.     private ConsumableBaseType _composite = new ComsumableBaseType();
    33.  
    34.     public string SomeProperty
    35.     {
    36.         get { return _composite.SomeProperty; }
    37.         set { _composite.SomeProperty = value; }
    38.     }
    39.  
    40.     public void Foo()
    41.     {
    42.         _composite.Foo();
    43.     }
    44.  
    45. }
    46.  
    47. public class SubTypeB : MaskableGraphic, IBaseType
    48. {
    49.  
    50.     private ConsumableBaseType _composite = new ComsumableBaseType();
    51.  
    52.     public string SomeProperty
    53.     {
    54.         get { return _composite.SomeProperty; }
    55.         set { _composite.SomeProperty = value; }
    56.     }
    57.  
    58.     public void Foo()
    59.     {
    60.         _composite.Foo();
    61.     }
    62.  
    63. }
    64.  
    If the consumable needs a reference to the concrete object that consumed it... just pass in a reference as 'IBaseType' in its constructor:

    Code (csharp):
    1.  
    2. public class ConsumableBaseType : IBaseType
    3. {
    4.  
    5.     private IBaseType _owner;
    6.     private string _value;
    7.  
    8.     public ConsumableBaseType(IBaseType owner)
    9.     {
    10.         _owner = owner;
    11.     }
    12.  
    13.     public string SomeProperty
    14.     {
    15.         get { return _value; }
    16.         set { _value = value; }
    17.     }
    18.  
    19.     public void Foo()
    20.     {
    21.         Debug.Log("DID FOO!");
    22.     }
    23.  
    24. }
    25.  
    Of course this doesn't enforce that 'IBaseType' is a MonoBehaviour.

    For this I usually use a contractual interface I call 'IComponent':
    https://github.com/lordofduct/spacepuppy-unity-framework/blob/master/SpacepuppyBase/IComponent.cs

    This way anything that implements it, must return some component for itself. May it be a wrapper, or a component itself.
     
    dschu, hopetolive and Kiwasi like this.
  4. Stephan-B

    Stephan-B

    Unity Technologies

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    Interfaces don't help since they cannot contain fields or implementation. This would still result in common fields and functions being duplicated in the sub classes.

    Code (csharp):
    1.  
    2. class BaseClass : MonoBehaviour
    3. {
    4.     public sting text { get; set; }
    5.  
    6.     protected void CommonFunction()
    7.     {
    8.          // Do stuff
    9.     }
    10.  
    11.     protected virtual void UniqueFunction() { }
    12.  
    13. }
    14.  
    15. class SubClassA : BaseClass
    16. {
    17.    
    18.      protected override void UniqueFunction()
    19.      {
    20.          // Do stuff unique to this sub class.
    21.      }
    22. }
    23.  
    24. class SubClassB : BaseClass, MaskableGraphic
    25. {
    26.  
    27.  
    28.      protected override void UniqueFunction()
    29.      {
    30.          // Do stuff unique to this sub class.
    31.      }
    32. }
    33.  
    P.S. @lordofduct I didn't see your post until after I posted. Let me consider your post.
     
    hopetolive and Ziplock9000 like this.
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Note, in your situation as well... you don't need to inherit from MaskableGraphic.

    Since MaskableGraphic is itself a component, and implements itself in the same component design pattern that unity set up (which is a composite pattern in and of itself).

    Instead you write your script as its own MonoBehaviour, and just include the 'RequireComponent' attribute on the script:
    http://docs.unity3d.com/ScriptReference/RequireComponent.html

    Code (csharp):
    1.  
    2. public class BaseType : MonoBehaviour
    3. {
    4.  
    5.     // common functionality
    6.  
    7. }
    8.  
    9. public class SubClassA : BaseClass
    10. {
    11.  
    12.     // Unique functionality & implementation
    13.  
    14. }
    15.  
    16. [RequireComponent(typeof(MaskableGraphic))]
    17. public class SubClassB : BaseClass
    18. {
    19.  
    20.     private MaskableGraphic _graphic;
    21.  
    22.     void Awake()
    23.     {
    24.         _graphic = this.GetComponent<MaskableGraphic>();
    25.     }
    26.  
    27.     //unique functionality & implementation, with access to '_graphic'
    28.  
    29. }
    30.  
    This honestly is the heart of the 'composite' design pattern. It reduces large inheritance chains, and instead allows adding/removing functionality based on the consumed parts that each type may or may want/need.

    See:
    https://en.wikipedia.org/wiki/Composite_pattern

    Unity just uses a modified version of the composite pattern usually called the 'component pattern' that is really common in the game design world.
     
    Stardog and Polymorphik like this.
  6. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Thanks to Unity's components, I don't really see the use case for multiple inheritance. At best, you can say it's a method to copy functionality without physically copying it, and in cases where you would need more control over execution order in the case of dependent components. The role of polymorphism is basically taken care of by interfaces.
     
  7. Stephan-B

    Stephan-B

    Unity Technologies

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    I need to inherit from MaskableGraphic as my component makes use of the functionality included in this class. Also you cannot add MaskableGraphic using [RequireComponent] since it is an Abstract class. You get the following error when doing so "Can't add script behaviour MaskableGraphic. The script class can't be abstract!"

    I also need to be able to use the GraphicRegistry.RegisterGraphicForCanvas(Canvas c, Graphic graphic).
     
  8. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    That is not entirely true. But you have to be careful the order you have added components to the game object. If there is a mono behavior that already implements the abstract class attached to the game object when you add the component that requires it, RequireComponent doesnt cry. Not great, but might be useful information?
     
  9. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,887
    Does MaskableGraphic have to be abstract? If so you could try this:
    Code (CSharp):
    1. public interface IMaskableGraphic
    2. {
    3.     void MaskableFunction();
    4. }
    5.  
    6. public abstract class MaskableGraphic : MonoBehaviour, IMaskableGraphic
    7. {
    8.     public abstract void MaskableFunction();
    9.  
    10.     public virtual void CommonFunction() { }
    11. }
    12.  
    13. // Component attached to GO's
    14. public class TypeOfMaskable : MaskableGraphic
    15. {
    16.     public override void MaskableFunction() { }
    17. }
    Code (CSharp):
    1.  
    2. public class BaseClass : MonoBehaviour { }
    3.  
    4. public class SubClassA : BaseClass { }
    5.  
    6. // TypeOfMaskable must be attached to same GO.
    7. [RequireComponent(typeof(TypeOfMaskable))] // Optional
    8. public class SubClassB : BaseClass
    9. {
    10.     private MaskableGraphic _maskable;
    11.  
    12.     void Awake()
    13.     {
    14.         _maskable = GetComponent<MaskableGraphic>();
    15.     }
    16.  
    17.     void Function()
    18.     {
    19.         _maskable.MaskableFunction();
    20.         _maskable.CommonFunction();
    21.     }
    22. }
    It's similar to lordofduct's post #5, except the RequireComponent would change to requiring a TypeOfMaskable component.
     
    Last edited: Nov 14, 2015
  10. Stephan-B

    Stephan-B

    Unity Technologies

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    MaskableGraphic is a Unity class so I can't change it.
     
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Here's the thing. Multiple Inheritance of classes does NOT exist in C#.

    You have to use interfaces.

    To share implementation, you have to use some type of compositing to get it.

    We've outlined multiple ways to consume shared implementation with interfaces.

    We don't know what exactly you want to do, so picking the BEST option is going to be difficult for us to do in our position of ignorance of your desires.

    So... pick one. Give it a try.

    Cause otherwise... you're not going to get multiple inheritance.
     
    Kiwasi likes this.
  12. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    How about another example for you... and from the sounds of what you need, how I probably would go, but again... I'm not sure exactly what you're attempting for.

    Code (csharp):
    1.  
    2. interface IBaseClass
    3. {
    4.  
    5.     void Foo();
    6.  
    7. }
    8.  
    9. class BaseClass : MonoBehaviour, IBaseClass
    10. {
    11.  
    12.     public void Foo()
    13.     {
    14.  
    15.     }
    16.  
    17. }
    18.  
    19. class SubClassA : BaseClass, IBaseClass
    20. {
    21.  
    22. }
    23.  
    24. [RequireComponent(typeof(BaseClass))]
    25. class SubClassB : MaskableGraphic, IBaseClass
    26. {
    27.  
    28.     private IBaseClass _other;
    29.  
    30.     void Awake()
    31.     {
    32.         _other = this.GetComponent<IBaseClass>();
    33.     }
    34.  
    35.     public void Foo()
    36.     {
    37.         _other.Foo();
    38.     }
    39.  
    40. }
    41.  
    SubClassB takes on the same behaviour as another BaseClass, by consuming it, and can still be treating polymorphicly as a IBaseClass.

    All of these suggestions though are all rooted in the same concept. It's just the ordering of the compositing that's changing around. You need to figure out the ordering that fits your needs.
     
  13. Frostbite23

    Frostbite23

    Joined:
    Mar 8, 2013
    Posts:
    458
    If C# doesn't support Multiple Inheritance. Then maybe try Javascript, though I'm not entirely sure if javascript does support it. Dunno Im really just eyeballing here :(
     
  14. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Nope, unityscript (the dialect of javascript used in unity) does not support multiple inheritance either.

    They both compile down to the same type of IL code to run in the Mono runtime. And the .Net/Mono runtime inherently does NOT support multiple inheritance of classes.

    And there's a very good reason why.

    Multiple inheritance complicates the member inheritance chain.

    Lets say for instance you have:

    Code (csharp):
    1.  
    2. public class OddType
    3. {
    4.  
    5.     public string Value
    6.     {
    7.         get;
    8.         set;
    9.     }
    10.  
    11.     public void Foo()
    12.     {
    13.         Debug.Log(this.Value);
    14.     }
    15.    
    16. }
    17.  
    18. public class EvenType
    19. {
    20.  
    21.     public int Value
    22.     {
    23.         get;
    24.         set;
    25.     }
    26.  
    27.     public void Foo()
    28.     {
    29.         Debug.Log(this.Value);
    30.     }
    31.    
    32. }
    33.  
    34. public class NeutralType : OddType, EvenType
    35. {
    36.  
    37.    
    38.  
    39. }
    40.  
    41.  
    42. //.....
    43.  
    44. NeutralType obj = new NeutralType();
    45. obj.Value = 5; //did I just set the string of OddType to "5" or the int of EvenType? Should it be type inferred?
    46. obj.Value = "Hello"; //if it were type inferred, this would be legit then?
    47. obj.Foo(); //so which gets printed to screen? 5, or "Hello"?
    48.  
    This is an issue that C++ multiple inheritance when dealing with abstract classes would have.

    Most project terms would restrict multiple inheritance to empty abstract classes that had no implementation... effectively making them an 'interface'. So as to avoid the implications of cross conflicts in the inheritance chain.

    Java and C# removed this complexity when designing their languages opting for the more explicit 'interface' contract type. And to require compositing to share implementation.
     
    Kiwasi and passerbycmc like this.
  15. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,739
    Ya in my experience with c++ and multiple inheritance, it is never not F***ed me. Works great if you are the only one on the team and can make sure there is no name clashing, and you got a effective way to pass data to the parents constructor. But with multiple people on the team it falls apart quick so i always just used it as a way to make fake interfaces.
     
  16. Stephan-B

    Stephan-B

    Unity Technologies

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    All the suggestions have / are very valuable. I just need to experiment with these various options.
     
  17. unitywlp

    unitywlp

    Joined:
    Jan 3, 2014
    Posts:
    146
    HI
    i am trying to add multiple base classes in c#.
    like in the example

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. using System.Collections;
    4. using Holoville.HOTween;
    5.  
    6.  
    7. public class animateflaot : MonoBehaviour , valuebasecointainer
    8.  
    9. {
    10. example code
    11.  
    12. }
    when i try to add mono behavior and another script i get error but i have seen some scripts with multiple base classes.

    if multiple base class was not possible how to add reference to another script so that i can add function from the valuebasecointainer can be called from the animateflaot script ?
     
  18. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,887
    Try just using a Component, or you can use an Interface.

    Add a ValueBaseContainer (MonoBehaviour) component to the same GameObject.
    Code (CSharp):
    1. public class AnimateFloat : MonoBehaviour {
    2.  
    3.     private ValueBaseContainer _valueBaseContainer;
    4.  
    5.     void Awake()
    6.     {
    7.         _valueBaseContainer = GetComponent<ValueBaseContainer>();
    8.  
    9.         _valueBaseContainer.FunctionName();
    10.         _valueBaseContainer.AnotherFunction();
    11.     }
    12. }
    Or you can make _valueBaseContainer public to click-drag a component from somewhere else into the inspector.
     
  19. unitywlp

    unitywlp

    Joined:
    Jan 3, 2014
    Posts:
    146
    what if the reference script was a name space, i cannot drag and drop into object how to do it in that case.
     
  20. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    356
    Namespace? Interfaces have issues within Unity due to their lack of serialization. They cannot be referenced from script.

    However in your case, just abstract ValueBaseContainer and inherit MonoBehaviour:
    Code (CSharp):
    1.  
    2. public abstract class ValueBaseContainer : MonoBehaviour
    3. {
    4. // abstract methods
    5. }
    6. public class animateflaot : ValueBaseContainer
    7. {
    8. // example code
    9. }
     
  21. olsonpm

    olsonpm

    Joined:
    Aug 25, 2017
    Posts:
    1
    Just because javascript doesn't allow multiple inheritance doesn't mean you can't gain the same functionality via other means.

    You would cleanly accomplish OP's use-case in javascript using:

    Code (JavaScript):
    1. class Base extends MonoBehavior {
    2.   // common behavior
    3. }
    4.  
    5. function makeMaskableGraphic(someClass) {
    6.   // add maskableGraphic functionality to `someClass` here
    7. }
    8.  
    9. function isMaskableGraphic(instance) {
    10.   // check if `instance` quacks like maskableGraphic
    11. }
    12.  
    13. class SubClassA extends Base {
    14.   // Unique functionality & implementation
    15. }
    16.  
    17. class SubClassB extends Base {
    18.   // Unique functionality & implementation
    19. }
    20.  
    21. makeMaskableGraphic(SubClassB);
    22.  
    23. export { SubClassA, SubClassB, isMaskableGraphic };
    24.  
    Now if I could just find a non-boilerplate way to do the same in c# -.-
     
  22. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    That's not valid UnityScript, so it won't run in Unity. UnityScript was* strongly typed the same way C# is.

    The closest equivalent is to use composition. Fortunately you are in luck. Unity's architecture is built around composition. You should be able to do the exact same thing with a couple of AddComponent and GetComponent calls.

    * I think its correct to refer to UnityScript in the past tense now.
     
  23. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    Not to mention this was a necro post and TextMesh Pro is now part of Unity, so... things have changed since then. :D
     
    vivalavida, hippocoder and Kiwasi like this.
  24. Jorhoto

    Jorhoto

    Joined:
    May 7, 2019
    Posts:
    99
    I wish we would have multiple inheritance, it is so powerful when it is well done... And for the issue you describe in C++, you got virtual inheritance.
    https://en.wikipedia.org/wiki/Virtual_inheritance
     
    FerdowsurAsif likes this.
  25. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
  26. Jorhoto

    Jorhoto

    Joined:
    May 7, 2019
    Posts:
    99
    To favour composition over inheritance may not be such a good/efficient practice in all cases, specially game programming.
     
  27. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    Good point. I don't know if the overhead is that large, though, but I'd love to see someone doing some performance tests. But there's overhead in multiple inheritance too. The point is moot, though, since C# doesn't support multiple inheritance.
     
    lordofduct likes this.