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
  4. Dismiss Notice

Resolved Implement getting class dynamically like GetComponent?

Discussion in 'Scripting' started by Magnesium, Apr 26, 2021.

  1. Magnesium

    Magnesium

    Joined:
    Sep 14, 2014
    Posts:
    178
    Hello,

    This might look trivial for people used to "evolved" programming languages, but how can i implement fetching a class in the same way that we call GetCompent<ComponentName> ? I have a list of getters for classes that implement the same interface and i would like to be able to fetch them by their class name in a single function.

    I am NOT trying get it by using a string.

    Thanks
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
    Fetching it from where?

    EDIT: I think perhaps you need to just use the typeof() the class as an index into a Dictionary of these classes?
     
  3. Magnesium

    Magnesium

    Joined:
    Sep 14, 2014
    Posts:
    178
    From a List. I mean that container would have a list of instantiated classes sharing an interface and it would get the requested one from the list, and list i mean the list type.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
    A list doesn't really have a way to fetch out of it. You could consider each item in the list and try cast it to the desired class type. Maybe some C# weenie here could help out with a more language-appropriate way of doing what you're trying to do.
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    You want to retrieve an instance from a list that is some type?

    Note that such a list could technically contain multiple objects of the same type. So in this case I'm going to assume you mean the first one since that's how GetComponent works.

    (in my pseudo-code I use T to represent the type in question...)

    As a loop:
    Code (csharp):
    1. T result;
    2. foreach(var item in myList)
    3. {
    4.     if(item is T o)
    5.     {
    6.         result = o;
    7.         break;
    8.     }
    9. }
    note this could be generalized into some function, even an extension method, for ease of reuse:
    Code (csharp):
    1. public static T FindFirstOfType<T>(System.Collections.IEnumerable e) //takes in an enumerable to work on all lists/arrays/other enumerables
    2. {
    3.     foreach(var o in e)
    4.     {
    5.         if(o is T result) return result;
    6.     }
    7.     return default(T);
    8. }
    You could also use linq:
    Code (csharp):
    1. var result = myList.OfType<T>().FirstOrDefault();
    Honestly you could go about it many ways but it's going to boil down to "loop over entries and find the one that matches your requirements". It's a list, that's how you do lists.
     
    Vryken, Bunny83, rubcc95 and 2 others like this.
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,539
    I just like to add that
    GetComponent<T>()
    is defined like this. Though ignore that "GetComponentFastPath" because it's just a fancy way to avoid the cast on the C# side with some native code pointer trickery. The generic version of GetComponent essentially just calls the non generic version like that:

    Code (CSharp):
    1. public T GetComponent<T>()
    2. {
    3.     return (T)(object)GetComponent(typeof(T));
    4. }
    Keep in mind that the actual list of components is stored on the native side in Unity. I'm pretty sure that Unity most likely just iterates through the list (probably a C++ vector) and search for the proper managed type. Since the number of components per object is rather small, you would not have any advantages using a Dictionary or hashtable. Though if you have many instances (50+), it may be worth using a dictionary. Though it just becomes much more complicated that way. The general approach looks something like that: Just assume we have a List of a base class or interface like that:

    List<IMyBase> components;


    This list essentially holds the instances that are "attached" / associated with our object. To get the first instance of a certain type, you just do this:

    Code (CSharp):
    1. public T GetComp()
    2. {
    3.     return (T)(object)GetComp(typeof(T));
    4. }
    5.  
    6. public IMyBase GetComp(System.Type aType)
    7. {
    8.     foreach (var obj in components)
    9.     {
    10.         if (aType.IsAssignableFrom(obj.GetType()))
    11.             return obj;
    12.     }
    13.     return null;
    14. }
    If you worry about performance because you are going to have many components, you could think about introducing a caching solution. However such a cache generally adds a bit housekeeping overhead andof course requires more memory to hold that extra data. As always trading
    speed <---> memory


    Of course the reason why Unity has the System.Type version is most likely due to the fact that they have to bridge the C# / C++ boundary and generics are a pain. So if you're looking for a pure C# solution, using the solution that @lordofduct posted is probably simpler. Though having a System.Type version is generally a good idea in case you need to automate the access to certain components. As I said generics are a pain to work with when you have dynamic requirements at runtime. For example when it comes to serialization.
     
    Last edited: Apr 27, 2021
    Magnesium likes this.
  7. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455

    Yiu can just create any object you want, add it to a static list and then use it anywhere you want by foreach loop you can use how many objects you want and then loop it to any list you want anywhere in your code.

    In case you want save this data i recommand you dont use unity specific thing like Vector 3 etc.
    Just use standard variables like float[] instead of vector 3 etc.

    Code (CSharp):
    1.     public ClusterObject(int clusterNumber, List<PlanetObject> planetList,float [] clusterPosition)
    2.         {
    3.  
    4.  
    5. Something like this
    6.             this.planetList = planetList;
    7.             this.clusterNumber = clusterNumber;
    8.             this.clusterPosition = clusterPosition;
    9.  
    10.             List<PlanetObject> getPlanetCountList = new List<PlanetObject>();
    11.             List<PlanetObject> getAsteroidCountList = new List<PlanetObject>();
    12.             List<PlanetObject> getMoonCountList = new List<PlanetObject>();
    13.  
    14.             List<int> clusterQuestCountList = new List<int>();
    15.             List<int> clusterFinishedQuestCountList = new List<int>();
    16.  
    17.             List<QuestObject> questObjectList = new List<QuestObject>();
    18.  
    19.             List<bool> clusterFinishedPLanets = new List<bool>();
    20.             foreach (PlanetObject item in planetList)
    21.             {
    22.                 objectCount = planetList.Count;
    23.                 if (item.planetType == "Planet")
    24.                 {
    25.                     getPlanetCountList.Add(item);
    26.                 }
    27.                 if (item.planetType == "Moon")
    28.                 {
    29.                     getMoonCountList.Add(item);
    30.                 }
    31.                 if (item.planetType == "Asteroid")
    32.                 {
    33.                     getAsteroidCountList.Add(item);
    34.                 }
    35.                 if (item.isFinished == true)
    36.                 {
    37.                     clusterFinishedPLanets.Add(item.isFinished);
    38.                 }
    39.                 questObjectList = item.questObjectList;
    40.                 if (planetList.Count == clusterFinishedPLanets.Count)
    41.                 {
    42.                     clusterStatus = 2;
    43.                 }
    44.  
    45.                 ///Cluster Status
    46.                foreach(QuestObject item2 in item.questObjectList)
    47.                 {
    48.                     if (item2.questStatus)
    49.                     {
    50.                         clusterFinishedQuestCountList.Add(1);
    51.                         Debug.Log(clusterFinishedQuestCountList.Count);
    52.                     }
    53.                    
    54.                 }
    55.                if(clusterFinishedQuestCountList.Count == 0)
    56.                 {
    57.                     clusterStatus = 0;
    58.                 }
    59.                 else if (clusterFinishedQuestCountList.Count > 0 && clusterFinishedQuestCountList.Count < planetList.Count * 5)
    60.                 {
    61.                     clusterStatus = 1;
    62.                 }
    63.                 else if (clusterFinishedQuestCountList.Count == planetList.Count * 5)
    64.                 {
    65.                     clusterStatus = 2;
    66.                 }
    67.          
    68.             }
    69.  
    70.  
    71.  
    72.  
    73.  
    74.             totalFinishedQuestCount = clusterFinishedQuestCountList.Count;
    75.             totalQuestCount = planetList.Count * 5;
    76.             planetCount = getPlanetCountList.Count;
    77.             asteroidCount = getAsteroidCountList.Count;
    78.             moonCount = getMoonCountList.Count;
    79.             planetIsFinished = clusterFinishedPLanets.Count;
    80.         }
     
  8. Magnesium

    Magnesium

    Joined:
    Sep 14, 2014
    Posts:
    178
    Perfect thanks. I don't worry too much about performances, those functions are just for a group of class to relate to each other.

    Code (CSharp):
    1. namespace Player {
    2. public class PlayerContainer {
    3.     // ....
    4.  
    5.     PlayerComponent[] components;
    6.  
    7.     public PlayerContainer() {
    8.  
    9.         components = new PlayerComponent[] {
    10.             new PlayerContactsObserver(),
    11.             // ...
    12.         };
    13.  
    14.         foreach (PlayerComponent component in components) {
    15.             component.InjectDependencies(this);
    16.         }
    17.     }
    18.  
    19.     public T GetComponent<T>() {
    20.         return (T)(object)GetComponent(typeof(T));
    21.     }
    22.  
    23.     public PlayerComponent GetComponent(System.Type aType) {
    24.         foreach (PlayerComponent component in components) {
    25.             if (aType.IsAssignableFrom(component.GetType()))
    26.                 return component;
    27.         }
    28.         return null;
    29.     }
    30.  
    31.     // ....
    32. }
    33.  
    34. public class PlayerContactsObserver : PlayerComponent {
    35.  
    36.     // ....
    37.  
    38.     public void InjectDependencies(PlayerContainer container) {
    39.         moveHandler  = container.GetComponent<PlayerMoveHandler>();
    40.         // ...
    41.     }
    42. }}
    What would be perfect now would be for the classes to fetch their dependencies in their constructors but this will be for another time.