Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Implementing my own Magic Methods

Discussion in 'Scripting' started by Maeslezo, Oct 19, 2016.

  1. Maeslezo

    Maeslezo

    Joined:
    Jun 16, 2015
    Posts:
    283
    Hello,

    I have read this very interesting post: https://blogs.unity3d.com/es/2015/12/23/1k-update-calls/

    Simonov said that:
    I think this is very interesing but I am not able to figure out how REALLY it is done in Unity. I have searched more information about this, but I don't know even what to look for.

    I would like to have my own magic methods, to control flow. I am doing something like this;

    Code (CSharp):
    1. interface ISomethingInFlow
    2. {
    3. SubscribeToFlow(IFlowManager);
    4. SomethingInFlow();
    5. }
    6.  
    7. class Foo:Monobehaviour, ISomethingInFlow
    8. {
    9. Awake()
    10. {
    11. SubscribeToFlow(flowManager);
    12. }
    13.  
    14. SomethingInFlow()
    15. {
    16. //do whatever
    17. }
    18. }
    With this approach, I subscribe the object in a flow manager list, and I call it when necessary.

    The real cool thing is that Unity does the same, but without any subscription, just defining the method. And they say they do it without reflection, with "inspected through scripting runtime".

    I am really curious about this, I can not imagine how Unity does this, so if anyone could contribute with a paper, post, link, small example or even a key word to do my own investigation, I would be very grateful.

    Thank you.
     
    SamFernGamer4k likes this.
  2. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    It's reflection, plain and simple. The results of the reflection are simply cached in a clever way.
     
    SamFernGamer4k and Ryiah like this.
  3. Maeslezo

    Maeslezo

    Joined:
    Jun 16, 2015
    Posts:
    283
    Simonov sais explicity that Reflection is not used.
    Indeed, I have found this post where the author says that is with something called CLR:
    http://creaturecoding.com/update-manager/
     
  4. Dave-Carlile

    Dave-Carlile

    Joined:
    Sep 16, 2012
    Posts:
    967
    The key part of that is "every time it needs to call one". It uses reflection once to identify the method and adds it to a list that it later iterates through. To my knowledge, reflection is the only way to programmaticaly identify a method by name at runtime.
     
    DonLoquacious, Ryiah and Kiwasi like this.
  5. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,738
    Why not just do it via interfaces, instead of needing to use reflection to do it via method name? Seems rather stupid to do it with magic names, when you can clearly define it via a interface. Also even unity is moving towards doing it all via interfaces, if you look at stuff like the IPointerClickHandler etc.
     
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    He says reflection is not used every time. Which makes sense. Once a script is written it stays the same. Scripts don't gain or loose methods at runtime.

    He then goes on to say that the script is inspected once and the results are cached. That inspection is called reflection.
     
  7. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Magic Methods are generally considered "bad". Unity themselves have admitted it was a mistake and have been using interfaces instead for all their newer code. Unless you have a really good reason, you should use interfaces instead of reflection. "I don't want to have to type the extra letters on the keyboard to include the interface name next to the class" is not a good reason. ;)
     
  8. Maeslezo

    Maeslezo

    Joined:
    Jun 16, 2015
    Posts:
    283
    I totally agree with you. And with the Simonov blog, It is absolutely clear that it is much better using interfaces. It is more a "scientific" curiosity.

    However, even with interfaces, my main question remains. For example, in ISerialization callback (https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.OnBeforeSerialize.html), the object doesn't need to register itself to any manager.

    So, where, when and how the object is registered to that "exotic list", to the method be called at the right moment?
     
    Last edited: Oct 20, 2016
  9. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Unity's core engine is written in C++, and that contains the Mono runtime. The Start and Update methods are called from somewhere in C++ land, so the reflection code to gather up all the references and call their magic methods is hidden there somewhere. If I were to guess, I'd think they automatically scan the .NET DLL's that get built from the Unity editor and look at the interfaces of every class inside of them, and if they're one of the correct types, they register it with some kind of receiver. You'd be able to do this in .NET if you were loading a different DLL, but I'm not sure if there's a way for an assembly or DLL to scan itself to see what classes it contains.
     
    Maeslezo likes this.
  10. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    "How" it works has been answered multiple times, including in the original article you linked: reflection. Though given that Unity messes around with the compiler I'm a little surprised they don't either output that information during the build, or pull it from the OBJ files or PDBs or something like that. (I'm also surprised that empty methods end up in the final build. I thought for sure the compiler would optimize those away, I can't think of a case where they'd ever be useful.)

    For my needs, at least, the simplest way to provide smiliar behavior is to just use delegates. Here is a very simple example. In this case any class (whether it's a MonoBehaviour or not) can add itself to a list of Start and Update events. Indeed, by registering multiple handlers, a single class could separate Update behaviors into different methods. It would also be easy to do things like add delegates for high- and low-priority delegates, for example, or time-delayed updates. And there's no reason these have to be limited to Unity events, custom events are simple to add as well (I often do this for state-machine change notifications).

    This way the only "magic method" ever used is Awake, and then strictly to register for these managed notifications. Note that clean removal is absolutely necessary to avoid memory leaks.

    Code (csharp):
    1. public class EventMgr : MonoBehaviour {
    2.  
    3.     public delegate void UnityStartClient();
    4.     public delegate void UnityUpdateClient();
    5.     public static UnityStartClient UnityStartClients;
    6.     public static UnityUpdateClient UnityUpdateClients;
    7.  
    8.     void Start()
    9.     {
    10.         if (UnityStartClients != null) UnityStartClients();
    11.     }
    12.  
    13.     void Update()
    14.     {
    15.         if (UnityUpdateClients != null) UnityUpdateClients();
    16.     }
    17.  
    18. }
    19.  
    Code (csharp):
    1. public class UIManager : MonoBehaviour {
    2.         void Awake()
    3.         {
    4.             EventMgr.UnityUpdateClients += UnityUpdate;
    5.         }
    6.  
    7.         public void OnDestroy()
    8.         {
    9.             EventMgr.UnityUpdateClients -= UnityUpdate;
    10.         }
    11.  
    12.         public void UnityUpdate()
    13.         {
    14.             // do something
    15.         }
    16. }
     
    SamFernGamer4k likes this.