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

Resolved How to use a variable in AddComponent<>() method ?

Discussion in 'Scripting' started by Stormer2020, May 19, 2023.

  1. Stormer2020

    Stormer2020

    Joined:
    Sep 14, 2020
    Posts:
    92
    Hi~ I want put a componentType in AddComponent<>().

    My code:

    Code (CSharp):
    1. GameObject resultObj = Instantiate(prefab, transform);
    2.    
    3.            string componentName = "SceneObjectBridge"; //it will a function param
    4.            var componentType = Type.GetType(componentName);
    5.            if (componentType != null)
    6.            {
    7.              
    8.                  // resultObj.AddComponent<typeof(componentType)>();
    9.                  // resultObj.AddComponent<componentType>();
    10.                  resultObj.AddComponent(componentType);
    11.  
    12.            }
    Why the componentType not add to gameObject?

    2.png
     
    Last edited: May 19, 2023
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,920
    Generic arguments can only take actual types. However the generic version of AddComponent is just a wrapper around the actual AddComponent method. That's the one you want to use. The generic method just does this:

    Code (CSharp):
    1. public T AddComponent<T>()
    2. {
    3.     return AddComponent(typeof(T)) as T;
    4. }
    The "actual" AddComponent method is not generic and takes a System.Type reference as an actual parameter. So you want to do

    Code (CSharp):
    1. resultObj.AddComponent(componentType);
     
    Stormer2020 likes this.
  3. Stormer2020

    Stormer2020

    Joined:
    Sep 14, 2020
    Posts:
    92
    Thank you! I updated the code,but the component not added to game object.

    //update, componentType is null. I'll see where the problem is.

    string componentName = "SceneObjectBridge";
    var componentType = Type.GetType(componentName);
    if (componentType != null)
    {
    //Have not print log, componentType is null
    Debug.Log("Add component: "+componentType.Name);

    // resultObj.AddComponent<typeof(componentType)>();
    // resultObj.AddComponent<componentType>();
    resultObj.AddComponent(componentType);
    }
     
    Last edited: May 19, 2023
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,920
    You essentially changed the question now and with the provided information we can not answer it. Are you sure that the type in question:
    • is actually derived from MonoBehaviour
    • is located in it's own file and the file name matches the class name, or the type is located in a loaded assembly?
    Do you actually enter your if statement? Keep in mind that Type.GetType will only find types from the executing assembly. If you use assembly definition files or if that class is allready located in a separate assembly, just using Type.GetType may not work. For that you need an assembly qualified type name that includes the assembly name or directly look for the Assembly and search for the type there.
     
  5. Stormer2020

    Stormer2020

    Joined:
    Sep 14, 2020
    Posts:
    92
    Wow~ Really thanks!

    I just fix it. Type.GetType(namespace.componentName)

    Code (CSharp):
    1. string componentName = "MyGame.SceneObjectBridge";
    2. var componentType = Type.GetType(componentName);    
    3.png
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,920
    May I ask why you actually want to use an unreliable string binding to add your component? Why don't you use the actual type? Is there a reason to go through the slow and error prone reflection method? Since it seems you placed your class in a namespace, you would simply do

    Code (CSharp):
    1. AddComponent<MyGame.SceneObjectBridge>();
    2. // or
    3. AddComponent(typeof(MyGame.SceneObjectBridge));
    Note that from a technical point of view the namespace is a fix part of the class / type name. inside code we can use "using" statements at the top in order to shorten type names in code and the compiler will figure out the actual class you want to refer to. That's why you get an error when you have two namespaces included that both contain the same class name. For example there is the UnityEngine.Random class and there's the System.Random class. When you import both, the UntiyEngine and the System namespace in the same file and try to use the Random class, the compiler will throw an error as it can not determine which class you actually want to use.
     
    MelvMay likes this.
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,324
    @Bunny83 beat me to the question as I was typing (no pun intended)! :)
     
    Bunny83 likes this.
  8. Stormer2020

    Stormer2020

    Joined:
    Sep 14, 2020
    Posts:
    92
    Beacause I need create a lots of gameObject with different component in runtime.
    Each type corresponds to a button in UI.
    Use UI event sent type string to the reflection method.
    I dont want write too much if/else and switch/case.
     
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    Ok, that's perhaps a legit use. Make sure you do this only once however. You can cache the types like this so can save yourself from having to get results by reflection all the time.
    Code (csharp):
    1. // using System.Collections.Generic;
    2. Dictionary<string, Type> _cache; // class member
    3.  
    4. Type getType(string typeName) {
    5.   _cache??= new Dictionary<string, Type>();
    6.  
    7.   Type type;
    8.   if(!_cache.TryGetValue(typeName, out type)) {
    9.     type = _cache[typeName] = Type.GetType(typeName);
    10.   }
    11.  
    12.   return type;
    13. }
    This doesn't save you from typing errors, so make sure your strings are good.

    Now you can
    Code (csharp):
    1. string componentName = "MyGame.SceneObjectBridge";
    2. var componentType = getType(componentName);
    Type.GetType will get called only the first time you use this, so we trade memory for performance. (Just to be in the clear: individual string length doesn't affect the speed btw and it doesn't slow down if you have many strings.)
     
    Last edited: May 19, 2023
    Stormer2020 likes this.
  10. Stormer2020

    Stormer2020

    Joined:
    Sep 14, 2020
    Posts:
    92
    Thank you~