Everts
Everts

Reputation: 10721

Init method only called once c#

Well, I know this has been asked already and I also happen to know the easiest way to do so.

Now my question is more about your advice on if there is a better way.

I want my method to be called only once when the component is enabled or created. See I can create a component but keep it disabled, then when I enable it for the first time, I want the Init method to be called. The component is contained into an "attached" object.

So I have the Component with

internal bool _runOnce;

then I have the MainObject

List<Component> _listComp = new List<Component>();
void Update(){
   foreach(Component c in _listComp){
      if(!c.enable)continue;
      if(c._runOnce){
          c.Init();
          c._runOnce = false;
      }
      c.Update();
   }
}

My main concern is that the check for _runOnce will happen every frame for every component on each object. I know it is just a boolean check and is worth nothing but I am just asking if anyone would know a pattern for this purpose that is better than this.

Thanks

Upvotes: 2

Views: 4953

Answers (4)

Lynn Crumbling
Lynn Crumbling

Reputation: 13367

You could also make a list of only the components thats are enabled....

List<Component> _listEnabled = _listComp.Where(item => (item.enable == true));

foreach(Component c in _listEnabled ){
   if(c._runOnce){
      c.Init();           // if at all possible, _runOnce should be set to false
      c._runOnce = false; // IN THE OBJECT, after calling Init();
   }
   c.Update();
}

Upvotes: 2

pescolino
pescolino

Reputation: 3133

My main concern is that the check for _runOnce will happen every frame for every component on each object.

From that I assume that you call Update very frequently. My concerns would be for the Update method of the components which is very likely to be much more expensive than a boolean check. This is called micro-optimization: You put a lot of effort into something that isn't a problem at all.

However, I would suggest to encapsulate initialization. Your MainObject does not need to know anything about it. _runOnce should be a private member of the component and enable should be a property (btw: _runOnce should be initialized to true somewhere). Each time your component is enabled you can check for _runOnce and call the initialization if needed:

public class MyComponent
{
    private bool _isInitialized; // I think this is a better name than _runOnce
    private bool _enable;

    public bool Enable
    {
        get
        {
            return _enable;
        }
        set
        {
            if (_enable == value)
            {
                return;
            }

            if (value == true && !_isInitialized)
            {
                Init();
            }

            _enable = value;
        }
    }

    private void Init()
    {
        // initialization logic here ...

        _isInitialized = true;
    }
}

Another idea would be to defer initialization to the Update method. This is basically what you already have but in an object oriented design:

public void Update()
{
    if (_enable && !_isInitialized)
    {
        Init();
    }

    // update logic here ...
}

Upvotes: 1

Tetsujin no Oni
Tetsujin no Oni

Reputation: 7375

I would question whether Update() is the appropriate lifecycle design to Init() your dynamic components.

It seems that it would make more sense to have a method notionally called GameObject.ActivateComponent(AbstractComponent C) call C.Init(), and AbstractComponent::Init call OnInit, which is what the components override and implement. AbstractComponent::Init would make the _runonce check and early-return. It's a method call, but it makes the code more abstract, and has the option to be extended to have a OnReinitialize code path later if you need to provide hooks for 'this is the second or later time I was init'd'. (Say, a statistics reset option...)

It definitely seems wrong for Update() to be probing an implementation detail like "runonce" to find out about the initialization state of Component.

List<Component> _listComp = new List<Component>();
void Update(){
   foreach(Component c in _listComp){
  c.Update();
   }
}

AbstractComponent

 public bool Enable { get;set;}
 private bool _initialized = false;

 void Update(){
     if (!Enable) return;
     Init();
     OnUpdate();
 }

 protected virtual void OnUpdate()
 {
 // filled in by user script
 }

 private void Init()
 {
 if (_initialized) return;
 OnInit();
 _initialized = true;
 }

 protected virtual void OnInit()
 {
 // filled in by user script
 }

Upvotes: 1

Derek
Derek

Reputation: 8783

What about just a regular constructor for Component?

public Component()
{
   Init();
}

Upvotes: 0

Related Questions