Search Unity

Generics why can't i do this?

Discussion in 'Scripting' started by jister, Feb 9, 2016.

  1. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public abstract class aAbility
    5. {
    6.     public abstract void SetUp<T>(T obj);
    7.     public abstract void Execute<T>(T obj);
    8.     public abstract void Exit<T>(T obj);
    9. }
    10.  
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. [System.Serializable]
    6. public class Ability : aAbility
    7. {
    8.     public List<GameInput> abilityInputs;
    9.     public override void SetUp<T>(T obj){}
    10.     public override void Execute<T>(T obj){}
    11.     public override void Exit<T>(T obj){}
    12. }
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [System.Serializable]
    5. public class Move : Ability
    6. {
    7.     float speed;
    8.     Vector3 direction;
    9.     Rigidbody body;
    10.  
    11.     public override void SetUp<RigidBody> (RigidBody obj)
    12.     {
    13.         body = obj;
    14.     }
    15.  
    16.     public override void Execute<Vector3> (Vector3 obj)
    17.     {
    18.         body.AddRelativeForce(obj*speed);
    19.     }
    20.  
    21.     public override void Exit<T> (T obj)
    22.     {
    23.         body.velocity = Vector3.zero;
    24.     }
    25. }
    26.  
    errors:
    Code (CSharp):
    1. Assets/Nick/Scripts/Abilities/Move.cs(14,17): error CS0029: Cannot implicitly convert type `RigidBody' to `UnityEngine.Rigidbody'
    2.  
    3. Assets/Nick/Scripts/Abilities/Move.cs(19,39): error CS0019: Operator `*' cannot be applied to operands of type `Vector3' and `float'
    4.  
    5. Assets/Nick/Scripts/Abilities/Move.cs(19,22): error CS1502: The best overloaded method match for `UnityEngine.Rigidbody.AddRelativeForce(UnityEngine.Vector3)' has some invalid arguments
    6.  
    7. Assets/Nick/Scripts/Abilities/Move.cs(19,22): error CS1503: Argument `#1' cannot convert `object' expression to type `UnityEngine.Vector3'
    8.  
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    That's not the way those T's work.

    The <T> is saying, calling code can define a type here. Not, inheriting code can constrain the type here.

    Generic constraints are done with the 'where' keyword.

    Code (csharp):
    1.  
    2. public void Foo<T>(T obj) where T : SomeType
    3.  
    And you can't override constraints, because it'd break polymorphism. If you had an instance of 'Move', but in a variable typed as 'aAbstract':

    Code (csharp):
    1.  
    2. aAbstract obj =  new Move();
    3. obj.SetUp... //what version of SetUp is being called?
    4.  
     
    aer0ace likes this.
  3. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    ok thanks, sounds very logic now that you say it.
    changed it to:
    Code (CSharp):
    1. public override void SetUp (params object[] objects)
    2.     {
    3.         foreach(object o in objects)
    4.         {
    5.             if(o is GameObject)
    6.             {
    7.                 GameObject obj = (GameObject)o;
    8.                 body = obj.GetComponent<Rigidbody>();
    9.             }
    10.         }
    11.     }
    12.  
    13.     public override void Execute(params object[] objects)
    14.     {
    15.         foreach(object o in objects)
    16.         {
    17.             if(o is Vector3)
    18.             {
    19.                 Vector3 v = (Vector3)o;
    20.                 body.AddRelativeForce(v*speed);
    21.             }
    22.         }
    23.     }
    24.  
    25.     public override void Exit(params object[] objects)
    26.     {
    27.         body.velocity = Vector3.zero;
    28.     }
    which works fine for what i needs.
    actually are there other ways to override overload?
     
    Last edited: Feb 10, 2016
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    You could give type parameters to the abstract type, to make typed subtypes:

    Code (csharp):
    1.  
    2. public abstract class Ability<T1, T2, T3> {
    3.     public abstract void SetUp(T1 obj);
    4.     public abstract void Execute(T2 obj);
    5.     public abstract void Exit(T3 obj);
    6. }
    7.  
    8. public class Move : Ability<Rigidbody, Vector3, float> {
    9.  
    10.     public override void SetUp(Rigidbody obj) {
    11.        ...
    12.     }
    13.  
    14.     public override void Execute(Vector3 obj) {
    15.        ...
    16.     }
    17.  
    18.     public override void Exit(float obj) {
    19.        ...
    20.     }
    21. }
    There's really no point to it, though - you wont be able to do anything interesting with an Ability here, since all of it's methods require a typed argument, so you can't really use the Ability without knowing which Ability it is, and what arguments it needs, which defeats the purpose of the inheritance.


    Overloading method parameters is possible:

    Code (csharp):
    1. void Foo(int i) {
    2.     print("It's an int!");
    3. }
    4.  
    5. void Foo(float f) {
    6.     print("It's a float!");
    7. }
    8.  
    9. void Start() {
    10.     int myInt = 4;
    11.     float myFloat = 4;
    12.  
    13.     Foo(myInt); //prints "It's an int!"
    14.     Foo(myFloat); // prints "It's a float!"
    15. }
    Though making an overload for a method from a supertype would probably be really confusing, and not do what you want;

    Code (csharp):
    1. public class A {
    2.     public void Foo(object o) {
    3.         print("Object foo");
    4.     }
    5. }
    6.  
    7. public class B : A {
    8.     public void Foo(int i) {
    9.         print("int foo");
    10.     }
    11. }
    12.  
    13. ...
    14.  
    15. B b = new B();
    16. A a = b;
    17. a.Foo(5); //prints "Object foo"
    18. b.Foo(5); //prints "int foo"
    The selection of which overloaded method will be used is done compile-time, not runtime - C# uses static dispatch.
     
    jister likes this.