- Home /
Add component in one line with parameters
I'm not sure I follow why components can't have constructors.
Is it just an enforced code design practice? If not, can we add a component with a one-liner constructor?
Is it to prevent some kind of bad initialization? But isn't AddComponent already allowed to be used only in the same places we could change the component's values anyway? If not, couldn't the constructor simply be limited to those cases?
In any case, I'm just wondering if we can shorten lines of code. So instead of writing something like this:
MyComponent myComponent = AddComponent<MyComponent>();
myComponent.MyInitialValue = "Initial Value";
We could write something like this:
MyComponent myComponent = AddComponent<MyComponent>("Initial Value");
Or at very least (trying to prevent horizontal scroll bars here):
MyComponent myC = AddComponent("MyComponent", "Initial Value") as MyComponent;
And, of course, allowing us to have as many parameters as we want, just like we would have on a regular constructor.
Just for reference, I've already shortened many lines of code by writing my own `GetOrAddComponent`.
Answer by cregox · May 01, 2013 at 06:54 PM
Using @dannyskim's suggestion, about Factory Design Patterns...
I'm using a slightly different way of implementing. I think doing it without reflection (like AddComponent("Initial Value")
) is basically impossible. Just get my GetOrAddComponent
code there and see if you can.
So, I've mixed up what I wanted with danny's and this is what I came up with:
public class MyComponent : MonoBehaviour {
string parameter = null;
public static MyComponent CreateComponent (GameObject where, string parameter) {
MyComponent myC = where.AddComponent<MyComponent>();
myC.parameter = parameter;
return myC;
}
And then use as such:
MyComponent myC = MyComponent.CreateComponent(gameObject, "Initial Value");
The difference here is that I don't create a GameObject
, just like AddComponent
does not as well. So you're expected to pass along one (again, just like the original), to which gameObject
is the current one, by code convention.
Answer by Eric5h5 · Apr 26, 2013 at 05:18 PM
AddComponent returns Component, so:
AddComponent<MyComponent>().myInitialValue = "Blah";
Interesting! But seems like this is 1/nth of an answer, since it can only reduce for 1 parameter and if we need the instance as suggested on the question, it's not even convenient to use! Although, in practice, it will be good enough many times. :-)
Answer by dannyskim · Apr 26, 2013 at 05:12 PM
I would suggest using a Factory Design Pattern:
http://answers.unity3d.com/questions/160610/passing-values-into-base-constructor-with-monobeha.html
Answer by Don-Tako · Oct 14, 2020 at 01:59 PM
Not a one line solution but clean enough without coding any extra methods....
MyComponent component = AddComponent<MyComponent>();
component .value1 = "blah";
component .value2 = "bleh";
component .value3 = "blih";
or
MyComponent component = obj.AddComponent<MyComponent>();
component .value1 = "blah";
component .value2 = "bleh";
component .value3 = "blih";
Answer by sisus_co · Dec 28, 2021 at 05:15 PM
It is possible to achieve this without needing to use any reflection - though it requires some ground work to be laid first:
1. Create an interface that defines a method that can be used to pass the argument.
2. Make the interface generic to allow defining the type of the argument in the implementing class.
3. Create an extension method that adds the component and then passes the provided argument through the method defined in the interface.
4. Create classes that implement the interface, assigning the received argument to a member field in the body of the method.
5. To allow passing multiple arguments create copies of the interface and extension method with multiple generic arguments.
So first you'll need something like this:
public interface IInitializableWithArgument<TArgument>
{
void Initialize(TArgument argument);
}
public class MyComponent : MonoBehaviour, IInitializableWithArgument<string>
{
public string myInitialValue;
public void Initialize(string myInitialValue)
{
this.myInitialValue = myInitialValue;
}
}
public static TComponent AddComponent<TComponent, TArgument>
(this GameObject gameObject, TArgument argument)
where T : MonoBehaviour, IInitializableWithArgument<TArgument>
{
var component = gameObject.AddComponent<TComponent>();
component.Initialize(argument);
return component;
}
After this creating components with arguments becomes easy as pie:
MyComponent myComponent = gameObject.AddComponent<MyComponent, string>("Initial Value");
To see this pattern being put to use in practice you can take a look at my own asset Init(args).
Note that one downside with this system is that the Awake and OnEnable event functions will get called before the Initialize method, so one needs to be careful inside these functions to avoid NullReferenceExceptions taking place (there are ways around this issue as well, but it requires some more complicated code).
Your answer
