Search Unity

Action and Delegates without instantiation.

Discussion in 'Scripting' started by hexdump, Feb 1, 2016.

  1. hexdump

    hexdump

    Joined:
    Dec 29, 2008
    Posts:
    443
    Hi,

    I would like to know if anyone found a way to build a publish/subscribe pattern avoiding memory allocations in C#.

    We know that when we assign a method to an action we are incurring in an allocation to wrap the method into an action bacause the Action is a reference type. In C++ we have function pointers but not in C#.

    In my case I have a messaging system that relies heavily on actions. So, any object can subscribe to listen to a game event.

    There are events like when an enemy dies, etc... that are triggered quite frequently so, I'm trying to search for an observer pattern that doesn't instantiates on the heap.

    For me it is not an option to pre allocate all methods as actions in the publisher class because they can be quite a bit.

    Has anyone found a way to accomplish this?

    Cheers!
     
  2. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Make class implement interface, send interface instead of delegate. Not as beautiful, but this way there can be no allocation. With delegates it's basically impossible in your case with your requirements.
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,539
    1) don't be afraid of allocations, they're often just necessary. Strings, arrays, etc, all are ref types and need to allocate on the heap. The components themselves are.

    2) If you're going to register/unregister the event frequently (say on the Update message or something), you can store the action in the Awake method, that's what I do to avoid doing it too frequently:

    Code (csharp):
    1.  
    2. public class Example : MonoBehaviour
    3. {
    4.  
    5.     private System.Action _someDel;
    6.  
    7.     void Awake()
    8.     {
    9.         _someDel = new System.Action(this.Foo);
    10.     }
    11.  
    12.     void Foo()
    13.     {
    14.         //do stuff
    15.     }
    16.  
    17.     void Update()
    18.     {
    19.         //register or unregister useing _someDel
    20.     }
    21.  
    22. }
    23.  
    3) There are various ways to receive callbacks that aren't event/delegate driven.

    For example there's the method most often used in Java (and used in Unity for the new UI events), where you define a callback interface and pass the actual object in.

    Code (csharp):
    1.  
    2. public class Example : MonoBehaviour, ISomeCallbackReceiver
    3. {
    4.  
    5.     void Start()
    6.     {
    7.         SomeMessenger.Register(this);
    8.     }
    9.  
    10.  
    11.     void ISomeCallbackReceiver.Signal()
    12.     {
    13.         //do stuff
    14.     }
    15.  
    16. }
    17.  
    18. public interface ISomeCallbackReceiver
    19. {
    20.     void Signal();
    21. }
    22.  
    23. public class SomeMessenger
    24. {
    25.  
    26.     public static void Register(ISomeCallbackReceiver receiver)
    27.     {
    28.         //do
    29.     }
    30.  
    31. }
    32.  
    Or reflection:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class BadMethodReference : MonoBehaviour
    6. {
    7.  
    8.     void Update ()
    9.     {
    10.         var d = BadDelegate.Create(this, "Foo");
    11.         d.Invoke();
    12.     }
    13.  
    14.     public void Foo()
    15.     {
    16.         Debug.Log("FOOOOOOO!");
    17.     }
    18.  
    19.  
    20.  
    21.  
    22.     public struct BadDelegate
    23.     {
    24.  
    25.         private object Target;
    26.         private System.Reflection.MethodInfo Method;
    27.  
    28.         public void Invoke()
    29.         {
    30.             Method.Invoke(Target, null);
    31.         }
    32.  
    33.  
    34.         public static BadDelegate Create(object target, string methodName)
    35.         {
    36.             if (target == null) throw new System.ArgumentNullException("target");
    37.  
    38.             var tp = target.GetType();
    39.             var meth = tp.GetMethod(methodName);
    40.             return new BadDelegate()
    41.             {
    42.                 Target = target,
    43.                 Method = meth
    44.             };
    45.         }
    46.  
    47.     }
    48.  
    49. }
    50.  
    All having various downsides to them as well.
     
  4. hexdump

    hexdump

    Joined:
    Dec 29, 2008
    Posts:
    443
    Thanks for all the answers.

    I have been using things like that myself to avoid allocations some times. Just, wanted to know if anyone had a hidden magical trick (that usually never exist :D) and was willing to share it.

    Cheers!
     
  5. Immanuel-Scholz

    Immanuel-Scholz

    Joined:
    Jun 8, 2013
    Posts:
    221
    Well.. if you are up to C# voodoo and black magic, you could try pooling the Action objects and set their (hidden) members via reflection. At the end of the day, even delegates are just classes..

    You can get the method pointer to a function from its MethodInfo. This should stay the same for all instances, so it can be cached per type.

    However, I didn't do this and I am not sure whether calling SetValue on the delegate will allocate by itself.. (If you try this, be sure to cache the object[] array parameter...)