Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

List/Array of child class references, but not actual instances of said references?

Discussion in 'Scripting' started by Polantaris, Jan 2, 2014.

  1. Polantaris

    Polantaris

    Joined:
    Jun 25, 2013
    Posts:
    18
    I have probably a weird question here, that's probably impossible but if not I would love to make use of it.

    I have a scenario in my project in which I have a base abstract class, and then multiple leaf classes that are built off of the base class. Any time I want to use a leaf, I can reference the base class, and since the abstract members of that base class are the same throughout the leaf classes, I can make use of any leaf class I have without knowing exactly which leaf class I'm working with.

    That's the background of what's going on, here's what I want to know: If I create an array of that base class, can I fill it with references to the leaf classes, but not actual instances of those leaf classes? The reason I want to do this is because I will never need all of those instances at once, but at certain points a random selection of those could be needed, based on player input. I don't really need to create instances of ones that are not in use, because I imagine that's a waste of resources that I could potentially avoid, however later down the line they may become in use, but that's not guaranteed. If in the end I would waste more resources handling the creation/destruction, then I'll just go about it the old fashioned way, however I'm not really sure if that's the case.

    Either way, it would be good for future reference if nothing else to know if this was possible. To re-iterate, what I'm wondering is if you can define an array using references to leaf classes without actually instantiating objects of those leaf classes, for later reference and, possibly then, instanciation.

    If more context is required, please let me know and I will provide it.
     
  2. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
    From the longer explanation I'm not sure what you want to do but you can:
    Code (csharp):
    1.  
    2. MyClass myClassRef = null;
    3.  
    Now you have a reference type variable that has not been assigned value nor any object of type MyClass has been created.

    You can make an array of such variables but array must have a defined type so it cannot hold references to objects of different types.


    Yes. You can define the table like so and assign it's elements objects of type derived from object (all classes do). In our case we use object keyword for array type but it could by some class.
    Code (csharp):
    1.  
    2.  object[] objects = new object[2];
    3.  objects[0] = new MyClass1();
    4.  objects[1] = new MyClass2();
    5.  
    P.S. This isn't Unity question at all assuming I understood it at all.
    https://www.google.com/#q=c#+can+you+assign+child+class+instance+to+base+class+reference+variable
    http://csharp.2000things.com/2011/0...le-can-refer-to-instances-of-derived-classes/


    EDIT:
    This: object[] objects = new object[2]; will allocate memory to hold 2 objects so it may not be what you're after since you mentioned something about saving resources. If you want to allocate only as much memory as you will need not knowing how much will that be then you shouldn't use arrays at all. You should use List maybe for variant length although I'm not sure it's the best choice from memory point of view. It has it's own overhead. Again this isn't Unity question but language question and it's pretty fundamental meaning Google is the place to go and not this forums.
     
    Last edited: Jan 2, 2014
  3. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    It will allocate the memory to hold 2 POINTERS, not 2 objects.

    This is an issue most people have in understanding a full object language like C#. Everything (except Value type) is handled as a pointer and not the object itself. What you pass around is only a memory address of where the real object is. This is why you can pass an object to a method and a new object is not created in the transfer.

    If I was to do this in C++:

    Code (csharp):
    1.  
    2. public void Method(MyType type)
    3. {
    4.     type.someValue = 10;
    5. }
    6.  
    "type" would be created and destroyed for the scope of that method. I would never have access to what was done in that method. I would have to do;

    Code (csharp):
    1.  
    2. public void Method(MyType* type)
    3. {
    4.     type->someValue = 10;
    5. }
    6.  
    This way, what I pass to the method is a pointer of MyType. Therefore, I modify the original object directly where it reside in the memory.

    In C#, the first example would have worked just fine, while the second is simply impossible. Pointers don't exist in C# (in a safe environment).

    Doing;

    Code (csharp):
    1.  
    2. // Allocates a memory address for "type" and the memory equal to sizeof(MyType) to hold the real object.
    3. MyType type = new MyType();
    4.  
    5. // Still have a memory address allocated, but it points towards "null".
    6. // The memory space for the real object is retrieved by the GarbageCollector if nothing is referencing it.
    7. MyType type = null;
    8.  
    In a Sys32, a memory address is a 32 bits pointer. In 64 bits, the address is 64 bits. So doing

    Code (csharp):
    1.  
    2. object[] list = new object[100];
    3.  
    Only allocates 100 * 32 bits or 100 * 64 bits. Obviously it cannot allocate any more than that, since it has no idea what size said "object" would take.
     
    Last edited: Jan 2, 2014
  4. Polantaris

    Polantaris

    Joined:
    Jun 25, 2013
    Posts:
    18
    No, it wasn't really directly related to Unity, however Unity has so many built in things that I never know what useful thing I'm missing due to inexperience with Unity could solve my problem.

    I know that I can make the first code example, but here's the issue: I have an array of BaseClass, and I don't know what LeafClass should be used without having some reference to it somewhere.

    You have the code example of :
    Code (csharp):
    1.  
    2.  object[] objects = new object[2];
    3.  objects[0] = new MyClass1();
    4.  objects[1] = new MyClass2();
    5.  
    However, how would I be able to store, in the class itself, that when I finally want to initialize objects[0], that I want a MyClass1? How do I know I don't want a MyClass8 or MyClass9, without initializing it?

    Basically, what I want to make is:
    Code (csharp):
    1. object[] objects
    2. object[1] = reference to MyClass1, but not actually an Instance as long as I don't need it.
    3. object[2] = reference to MyClass 2, but not actually an Instance as long as I don't need it.
    4. etc.
    That's why I figure it's probably impossible without manually defining each of the 10 objects as 10 different variables. Which, sure I could do. Overall this is more of a hypothetical, "Can you?" question, because I can certainly work around not being able to.
     
  5. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    What? Why would you want to know that object[0] will one day be an instance of MyClass1? Why not make the instance right away?

    But let's say you really want that... You could simply have a list of type of the instance you will one day want;

    Code (csharp):
    1.  
    2. Type[] types;
    3.  
    and create an instance with the Activator;

    Code (csharp):
    1.  
    2.  
    3.  
    4.  object[] objects = new object[types.Length];
    5.  objects[0] = Activator.CreateInstance(types[0]);
    6.  
    7.  
    Frankly, no idea why you would want that.
     
  6. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    You can... It's called Polymorphism. What I would do if I were you is just create a method as part of the base class that will return an instance of the new class... you can also cache and reuse these if needed. Of course you'd have to change this up depending on whether you need multiple instances, just one (on demand), or need to clean them up... but here's a simple example. Let's say you have:

    Base Class called MyBaseClass
    Class called FirstDerived that inherits from MyBaseClass
    Class called ThirdDerived that inherits from MyBaseClass
    Class called FourthDerived that inherits from MyBaseClass

    So you could set it up as follows:

    Code (csharp):
    1.  
    2. public abstract class MyBaseClass
    3. {
    4.       private static Dictionary<type, MyBaseClass> Instances = new Dictionary<type, MyBaseClass>();
    5.  
    6.  
    7.       //some abstract method you're sharing
    8.       public abstract void MethodOne() {}
    9.  
    10.       public T GetInstance<T>() where T : MyBaseClass, new()
    11.       {
    12.              var desiredType = typeof(T);
    13.  
    14.              if(MyBaseClass.Instances.ContainsKey(desiredType)
    15.                   return MyBaseClass.Instances[desiredType] as T;
    16.  
    17.              var result = new T();
    18.              MyBaseClass.Instances.Add(desiredType, result);
    19.              return result;
    20.        }
    21. }
    22.  
    23. //Just creating dummy classes below
    24. public class FirstDerived : MyBaseClass
    25. {
    26.       public FirstDerived () {}
    27.  
    28.       public override void SomeMethod() {}
    29. }
    30.  
    31. public class SecondDerived : MyBaseClass
    32. {
    33.       public SecondDerived () {}
    34.  
    35.       public override void SomeMethod() {}
    36. }
    37.  
    38. public class ThirdDerived : MyBaseClass
    39. {
    40.       public ThirdDerived () {}
    41.  
    42.       public override void SomeMethod() {}
    43. }
    44.  
    45.  
    Now you have a dictionary that stores instances of MyBaseClass (actually subclasses since it's abstract). You have a GetInstance method that takes a type argument "T" which is constrained to subclasses with a parameterless constructor and that inherit from MyBaseClass. When you call GetInstance<T> it checks the cache to see if an instance exists... if it does it returns it, otherwise it creates one, caches it and returns the result. Of course you'll also want a way to clean up those instances if you need to. But here's an example of how you'd get a derived instance:

    Code (csharp):
    1.  
    2.  
    3. //Get an instance from the static method on the base class
    4. var someInstance = MyBaseClass.GetInstance<SecondDerived>();
    5.  
    6.  
    This way you're only creating them if you need them and caching them.
     
    Last edited: Jan 2, 2014
  7. Polantaris

    Polantaris

    Joined:
    Jun 25, 2013
    Posts:
    18
    I'm making character classes, in which it learns Y number of abilities, but can only use X. (Y is always greater than X). They can be switched around at certain locations in the game, but at any point in play-time, only X will be active. If I can only use X of them, that means that I will have Y-X left over that are not in use at any given time. If I don't need them, why create an Instance of them? They're not being used, and may not be for hours of run-time, potentially never if the player never wants to use it. But, the class still has the abilities on list, able to be switched to at safe locations. They're set in stone (Class A always learns those Y abilities), so at some points in run time, if the player decides they want ability 7, then they need to be able to pull it up on demand, but if they never want ability 7, then I'm wasting resources by having an object for ability 7 in existence when it won't be accessed.

    Maybe I'm going about it the wrong way.
     
  8. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Well... Yeah, I guess it's a good reason to store types instead of objects. So,

    Code (csharp):
    1.  
    2. public List<Type> LearnedAbilities;
    3. public List<object> AvailableAbilities;
    4.  
    Just so you know, if you need some serialization at some point, Unity is unable to serialize "Type".
     
  9. Polantaris

    Polantaris

    Joined:
    Jun 25, 2013
    Posts:
    18
    Thanks for the info and help. I'm gonna have to look up Serialization, I've never really needed it before. If you have a good source of information in regards to it, please let me know, I'd really appreciate it. I've been using [System.Serializable], but that's mostly to get certain classes to appear in the Inspector, I haven't really used it outside of that.
     
  10. Polantaris

    Polantaris

    Joined:
    Jun 25, 2013
    Posts:
    18
    When defining the List<Type>, should I assign members as "typeof(whatever)"?

    So,
    Code (csharp):
    1.  
    2. public List<Type> LearnedAbilites;
    3. LearnedAbilities.Add(typeof(Ability1));
    4. LearnedAbilities.Add(typeof(Ability2));
    5. //etc.
    6.  
    Is this technically correct, or is there a better function/method to assigning the values?
     
  11. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    It's correct.
     
  12. Polantaris

    Polantaris

    Joined:
    Jun 25, 2013
    Posts:
    18
    Thanks a lot for the help.