Search Unity

'new' keyword in class member

Discussion in 'Scripting' started by Polymorphik, Apr 27, 2017.

  1. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    Hello,

    I am working on my own in-house Input system and so far so good. I had some logic that was the same for different APIs that I pulled out and made a static method for base classes to use. The static function takes in an object GamePad.

    GamePad contains members that will be overridden by UnityGamePad who's members have the same name but different type.

    Example.
    Code (CSharp):
    1.     [System.Serializable]
    2.     public abstract class GamePad : System.Object {
    3.         public GamePadMetaData MetaData = new GamePadMetaData();
    4.  
    5.         public GamePadAction Action01 = new GamePadAction();
    6.         public GamePadAction Action02 = new GamePadAction();
    7.         public GamePadAction Action03 = new GamePadAction();
    8.         public GamePadAction Action04 = new GamePadAction();
    9. ....etc

    Code (CSharp):
    1.     [System.Serializable]
    2.     public class UnityGamePad : GamePad {
    3.         public List<string> SupportedDevices;
    4.  
    5.         public new UnityGamePadAction Action01;
    6.         public new UnityGamePadAction Action02;
    7.         public new UnityGamePadAction Action03;
    8.         public new UnityGamePadAction Action04;
    9. ..... etc

    InputDevice has the protected static function with signature
    Code (CSharp):
    1.         protected static string GetGlyph(GamePad GamePad, GamePadCode code) {
    2.             // logic here
    3.         }
    UnityInputDevice derives from InputDevice and calls InputDevice.GetGlyph() when the UnityInputDevice object's method gets called.

    In Windows this works great and even when running on Android. I am testing on OS X and the values returned are always empty. I tried rewriting the method signature as follows

    Code (CSharp):
    1.         protected static string GetGlyph<T>(T gamePad, GamePadCode code) where T : GamePad {
    2. // Logic here..
    3. }
    But I get the same result.

    It seems that whenever I call gamePad.Action01, for example, it's referring to GamePad rather than the overridden 'new' keyword from UnityGamePad.

    Am I missing something here for OS X?

    Thanks in advance.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    I would advise this as being a bad design choice.

    The fields in GamePad will still technically exist. All the 'new' command is doing here is saying "let there be a colliding name, when we reference the object typed as this child class we will return the 'new' members, but when we reference the object as its base class we will return the original base members".

    Essentially if you do:

    Code (csharp):
    1.  
    2. GamePad pad = new UnityGamePad();
    3. var act = pad.Action01;
    4.  
    In this case, we're reference the GamePadAction from the GamePad, NOT the 'new' UnityGamePadAction from the UnityGamePad.

    This creates all sorts of weird conflicts about the place.

    The fact that this is how 'new' acts creates weird conflicts at times, and honestly in 10 years I've found all of 2 times that I really really needed it... all else I usually had a much better way of doing such things. And those 2 times I really needed to use it, it was because it was code maintenance and refactoring would have taken far too much time... and enterprise software is about getting the fix in NOW, not later.

    If you're using the 'new' keyword for your original design... your design is probably lacking.
     
    Kiwasi and Polymorphik like this.
  3. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    While true, it worked in Windows and Android which is why I don't understand why it didn't work in OS X*. Also by using the Generic T method it should have worked as T gamePad is now UnityGamePad or w/e but we are guaranteed it is of type GamePad by (where T : GamePad). I'm in the process of rewriting it anyways but for different APIs its worked so far doing it this way but its not ideal.
     
  4. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    There's no point to using "new" here. If UnityGamePadAction derives from GamePadAction simply leave the fields as is and remove those field definitions in UnityGamePad.

    Remember Liskov's substitution principal. make the fields in the base class the most abstract type that's relevant. you can put a UnityGamePadAction instance into GamePadAction field just fine. then you leave it up to the deriving class to use the proper concrete types in said slots.

    Code (CSharp):
    1. [System.Serializable]
    2.     public class UnityGamePad : GamePad
    3.     {
    4.         public List<string> SupportedDevices;
    5.  
    6.         public UnityGamePad()
    7.         {
    8.             Action01 = new UnityGamePadAction();
    9.             Action02 = new UnityGamePadAction();
    10.             Action03 = new UnityGamePadAction();
    11.             Action04 = new UnityGamePadAction();
    12.         }
    13.     }
    14.  
    also if you want these fields to be serialized properly through polymorphism then you'll likely want GamePad and GamePadAction to be ScriptableObjects. The "new" modifier wouldn't solve this anyway, plus member hiding is just an ugly band-aid.
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    If you have code that behaves differently between OSX and Windows (that's not file-system related), that's a bug! You should file a bug report.
     
    Polymorphik likes this.
  6. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    I redesigned my pattern. Its much cleaner and works without being all 'hacky'. The 'new' keyword was just convenient as it got it to work but I never liked it.

    For anyone wondering I used Generics and got the results I was looking for and made use of polymorphism.

    New pattern I went with. Cheers guys.
    Code (CSharp):
    1. [System.Serializable]
    2. public class GamePad<Action, Stick> : System.Object where Action : GamePadAction where Stick : GamePadStick {
    3.     public Action Action01;
    4.     public Stick LStick;
    5. }
    6.  
    7. [System.Serializable]
    8. public class GamePadAction : System.Object {
    9.     public string Name;
    10. }
    11.  
    12. [System.Serializable]
    13. public class GamePadStick : System.Object {
    14.     public string Name;
    15. }
    16.  
    17. [System.Serializable]
    18. public class UnityGamePadAction : GamePadAction {
    19.     public float Value;
    20. }
    21.  
    22. [System.Serializable]
    23. public class UnityStick : GamePadStick {
    24.     public UnityEngine.Vector2 Value;
    25. }
    26.  
    27. [System.Serializable]
    28. public class UnityGamePad : GamePad<UnityGamePadAction, UnityStick> {
    29.     void Start() {
    30.  
    31.     }
    32. }
    33.  
    34. [System.Serializable]
    35. public class InputDevice<T, Action, Stick> : System.Object where Action : GamePadAction where Stick : GamePadStick where T : GamePad<Action, Stick> {
    36.     public T gamePad;
    37.  
    38.     public virtual void Set(T gamePad) {
    39.         this.gamePad = gamePad;
    40.     }
    41. }
    42.  
    43.  
    44. [System.Serializable]
    45. public class UnityDevice : InputDevice<UnityGamePad, UnityGamePadAction, UnityStick> {
    46.     public override void Set(UnityGamePad gamePad) {
    47.         base.Set(gamePad);
    48.     }
    49. }
     
  7. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    In unity when i expose UnityDevice I see UnityGamePad for the member gamePad stuff such as the string and float values.
     
  8. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    Yeah, I was actually surprised it worked for me on* Windows/Android at all. I'm glad it didn't work on OS X as I redesigned my input system.
     
  9. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    The problem here is UnityGamePad is just a blueprint that defines input IDs, DeadZones etc. It will be used by UnityDevice which expects certain definitions be in UnityGamePad. If I do it the way you are saying, which is not wrong, I would have to type cast everytime I wanted to expose the derived information in UnityGamePadAction that does not exists in GamePadAction.
     
  10. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    yup, and it works completely fine.

    Using Generics here works even better. its much cleaner. but when you can't use generics, typecasting at runtime is more reliable. Especially if the base class is something you cannot modify, or for instance you happen to be writing in another language that doesn't support generics. however neither applies here.