x


Invoking all instances of a non static function...

How can I simultaneously invoke all instances of a function without it being a static function?

more ▼

asked May 12 '12 at 10:21 PM

Tofudude624 gravatar image

Tofudude624
165 28 69 94

(comments are locked)
10|3000 characters needed characters left

2 answers: sort voted first

You can't invoke "all instance methods". The memory addresses of those functions are not known until runtime, so the CLR wouldn't know what to invoke where. The whole reason that you can link against a static function is that its memory address is known when the compiler generates the code.

With that said, you can use events and delegation to create a broadcasting class that all your objects subscribe to. When the broadcaster invokes its event, all the listener get the message, which sounds exactly like what you want to do. Here's an example:

  1. Create an empty scene.
  2. Add one gameobject called "BroadcastObject".
  3. Add a few listener objects, their name doesn't matter.

Attach the following script to the BroadcastObject:

using UnityEngine;
using System;

public class Broadcaster : MonoBehaviour
{
    // We use this to broadcast
    public event BroadcastHandler Tick;

    // These are the arguments that are broadcast (in our case they're empty)
    public EventArgs e = null;

    // Instantiate the EventHandler
    public delegate void BroadcastHandler(Broadcaster b, EventArgs e);

    // Let's broadcast a message
    void OnGUI()
    {
        if (GUI.Button(new Rect(20, 20, 110, 20), "Call all listeners"))
        {
            // Check if we have subscribers
            if (Tick != null)
            {                  
                Tick(this, e);
            }
        }
    }
}

Attach this script to the Listeners:

using UnityEngine;
using System;

public class Listener : MonoBehaviour
{
    void Start ()
    {
    // Find the broadcaster's GameObject and retrieve the script
    GameObject obj = GameObject.Find ("BroadcastObject");

    if (obj != null)
    {
        Broadcaster b = obj.GetComponent<Broadcaster>();

        if (b != null)
        {
            // Subscribe to the broadcaster. Wire up the MessageReceived
            // function to be called when a message is received.
            // (Use -= to unsubscribe later, if desired)
            b.Tick += new Broadcaster.BroadcastHandler(MessageReceived);
        }
    }
    }

    private void MessageReceived(Broadcaster b, EventArgs e)
    {
        // Invoke your actual method here
        Debug.Log(this.ToString() + "Message received!");
    }
}

That should do it. When you press the GUI button, all listeners subscribed to the broadcaster receive the event. In this case, all write "Message received!" to the console. If you want to send actual arguments to each listener, create a custom EventArgs class that carries those parameters. A use case might be an event-driven NPC scheduler for an RPG game. Rather than having each NPC check the time every frame, you instead have them subscribe to the time-of-day broadcaster, which sends an event whenever the the time has meaningfully changed ("Time to go to bed!") via the EventArgs.

The standard event pattern in C# can take a while to grok. Try it out in an empty project first, then adapt it for your project step by step. I can post the example project if needed.

more ▼

answered May 12 '12 at 11:32 PM

Drakestar gravatar image

Drakestar
916 6 7 14

That is what I have been looking for.

Thank you for sharing.

Scripts work well but I wonder if I have an abstract class for Broadcaster, how can I reach to event?

Apr 25 at 09:24 AM emrahsifoglu
(comments are locked)
10|3000 characters needed characters left

There is one way though. It's a pattern I call the InstanceList pattern.

I've written about the military pattern which is a special use case for the InstanceList pattern. It enables all instances to be called through one static function.

The basic idea is to have a list of all instances, then through a static function; call a method on all instances. Here is an example:

public abstract class Fireable : IDisposable {
    public static void Fire() { 
        foreach (var fireable in instanceList) { 
            fireable.OnFire(); 
        } 
    }

    public void Dispose() { instanceList.Remove(this); } 

    protected abstract void OnFire(); 

    protected Fireable() { instanceList.Add(this); }

    private static List<Fireable> instanceList = new List<Fireable>();
}

public class Gunner : Fireable { 
    public Gunner(string name) { Name = name; }

    protected override void OnFire() { Console.WriteLine("\tGunner "+Name+" firing..."); }

    public string Name { get; set; }
}

Given:

Gunner g1 = new Gunner("Rambo"), 
       g2 = new Gunner("James"),
       g3 = new Gunner("Lara"),
       g4 = new Gunner("Robocop");

Then:

Fireable.Fire();

Would cause:

Gunner Rambo firing...
Gunner James firing...
Gunner Lara firing...
Gunner Robocop firing...

I have made the InstanceList pattern available for download through NuGet InstanceListPattern

more ▼

answered Apr 25 at 10:03 AM

alexanderbrevig gravatar image

alexanderbrevig
116 1 3

Thank you. It is very useful.

Apr 25 at 07:44 PM emrahsifoglu

Here's a version that supports this on MonoBehaviours:

using System.Collections.Generic;
using UnityEngine;

public class MonoCollection<T> : MonoBehaviour where T : MonoCollection<T>
{
    public static List<T> all = new List<T>();

    protected virtual void OnEnableEx()
    {
    }

    protected virtual void OnDisableEx()
    {
    }

    protected  void OnEnable()
    {
        all.Add(this as T);
       OnEnableEx();
    }

    protected  void OnDisable()
    {
        all.Remove(this as T);
       OnDisableEx();
    }
}

Any script which inherits from MonoCollection has an all static member that can be used to access the instances.

   public class SomeBehaviour : MonoCollection<SomeBehaviour>
   {
        pubic void SomeFunction()
        {
        }
   }

The to call all methods:

   foreach(var item in SomeBehaviour.all)
        item.SomeFunction();
Apr 26 at 06:21 AM whydoidoit
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x477

asked: May 12 '12 at 10:21 PM

Seen: 557 times

Last Updated: Apr 26 at 06:21 AM