Search Unity

Delegate serialization

Discussion in 'Scripting' started by ufo_driver, Aug 5, 2011.

  1. ufo_driver

    ufo_driver

    Joined:
    Jul 6, 2011
    Posts:
    93
    I have made custom inspector for my class with ability to manage delegates. Everything works perfect in edit mode but when I run play mode all delegate values are reset to default. All these fields are marked with SerializeField attribute and whole class is also marked with Serializable attribute. Are delegates serializable in Unity? It is really important question as I wrote many code around this without even thinking that I should have tested delegate serialization at first :(
     
  2. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    No.

    If you don't see it in the Inspector, and it's not using [HideInInspector], then it's not serialized, even if you give it [SerializeField] or make it public.

    All of my delegates and events are set up in Awake, for this reason. (Unless of course they'll be reassigned during gameplay, but even then, they'll get an initial value in Awake.)
     
  3. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    That's not entirely true Jessy, at lest not in 3.5 (haven't checked it before). I've managed to serialize a public delegate. The problem is that many people think that Awake is not executed after you hit play - and it is. So they assign a null value to their delegates (so as to have an initialized delegate) into awake and... well, you guess the rest. The proper way (or at least an elegant way) to do it is to have a delegate initialization method, and call it from Awake only if the delegate is null, this way it won't be initialized again during play. Something like this:

    public void Awake() {
    if (UpdateAction == null) InitializeUpdateDelegate();
    }

    public void InitializeUpdateDelegate() {
    UpdateAction = RedrawCanvas;
    }

    Edit: For the record I've used the word "serialize" mistakenly last year, I confused it with "persist", as in keeping it while getting in and out of play mode. I don't see much of a reason to actually serialize (as in "between Unity sessions") a delegate, I simply initialize it with some method when applicable - but I might be wrong and you could come up with some interesting example.
     
    Last edited: Mar 14, 2013
  4. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    You mean:

    Code (csharp):
    1. public void Awake() {
    2.    UpdateAction = UpdateAction ?? RedrawCanvas;
    3. }
     
  5. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I don't believe you; let's see it!

    I realize that many people don't RTFM, but I don't think they'd be reading this thread. :p


    Why would you bother reassigning to null?
     
    eytienne likes this.
  6. Tseng

    Tseng

    Joined:
    Nov 29, 2010
    Posts:
    1,217
    What's the point of doing this? The Awake will only be called once: When the object is created, and never again. When you destroy the object and create a new one, you will have to assign it again, on each instance. The event will always be null in your case, when an Object is created, unless it's static.

    If the event is part of some other Object, it makes no sense at all to check against null, because it doesn't matter.

    And static events/delegates are to be threated carefully in order to prevent serious memory leaks.

    The only reason ever, where you must check an event against null is before calling it

    Code (csharp):
    1.  
    2.     if(UpdateAction != null)
    3.          UpdateAction(...);
    4.  
    But honestly, I don't know of any reason why someone would want to have delegates serialization in the Inspector. Maybe a Dictionary<string, Delegate> would be better for your case? You could assign a "event" string in the inspector and do something like

    events[myEventString] += MyAction;

    where myEventString is set in the inspector.

    http://msdn.microsoft.com/en-us/library/z4ka55h8(v=vs.100).aspx
     
  7. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    ???
     
  8. Tseng

    Tseng

    Joined:
    Nov 29, 2010
    Posts:
    1,217
    Guess he didn't got it that

    Code (csharp):
    1.  
    2. public void Awake() {
    3.    UpdateAction = UpdateAction ?? RedrawCanvas;
    4. }
    5.  
    is equal to

    Code (csharp):
    1. public void Awake() {
    2.    if(UpdateAction==null)
    3.       UpdateAction = RedrawCanvas;
    4. }
    5.  
    XD
     
  9. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Really? I was under the impression that it meant
    Code (csharp):
    1. UpdateAction = UpdateAction == null ? RedrawCanvas : UpdateAction;
    Functionally equivalent, but I was under the impression that reassigning the same pointer is both an unnecessary use of CPU time, and less clear code. I'm also saying that UpdateAction will always be null (in the non-static case). I realize what I said could have been worded better; UpdateAction == null is the one case where there would not be an unneeded reassignment. It's complicated to talk about, because the code assumes a falsity, as far as I know.
     
    Last edited: Mar 1, 2012
  10. Tseng

    Tseng

    Joined:
    Nov 29, 2010
    Posts:
    1,217
    Typically it is, but the case above is special in so far, that UpdateAction is on both sides (left and right). Also there is no reason to be concerned about a cpu cycle more or less, when this code is inside of a method which is only called once or rarely.

    Usually the compiler should take this into consideration that left and right side of the equal sign are the same and apply, otherwise JIT could do it (even though, Mono JIT - especially the outdated Mono used in Unity - is not as sophisticated as the .NET one).
     
  11. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    As far as I'm concerned, JIT doesn't exist for Unity use. ;)
     
  12. George Foot

    George Foot

    Joined:
    Feb 22, 2012
    Posts:
    399
    Hmm, looking at the generated code in Reflector gives some interesting results! For the one-liner, it does indeed emit a redundant store. But the version with an "if" statement is just as long, and includes a call to the inequality operator. Ick. I guess Mono's code generation really is quite bad sometimes.
     
  13. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Whoa - 'unnecessary use of CPU time'? What are you on? At the very worst, assuming crappy compilers, we're copying one pointer to another. If that's going to have a measurable performance impact on your game then you need to get off of your abacus!

    In terms of less clear code, compared to the alternative it is much clearer.

    That's an entirely separate issue. If UpdateAction is always null then any method of checking for null is redundant. If it might not always be null, and you want ensure a default is assigned in the cases where it is null, the ?? operator exists.
     
  14. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I don't agree. The former suggests that we always plan to make an assignment; the latter says exactly what we mean. It doesn't matter that the copying is negligible; Earth creatures form habits and lifestyles, and being negligent here can lead to unintentional mass extinctions or subpar suppers.
    Code (csharp):
    1. UpdateAction = UpdateAction ?? RedrawCanvas;
    2. if (UpdateAction == null) UpdateAction = RedrawCanvas;
     
    Last edited: Mar 2, 2012
  15. jedy

    jedy

    Joined:
    Aug 1, 2010
    Posts:
    579
    Anyone got this working in Unity 4?
    I've personally failed a couple of times to serialize any kind of delegate.
     
  16. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    Actually no, I didn't mean that since, at least in Unity 3.5, in some circumstances the ?? operator simply doesn't work, you have to explicitly compare with null. That's one of those situations, and I could only find it through trial-and-error.

    PS.: For the record I've used the word "serialize" mistakenly last year, I confused it with "persist", as in keeping it while getting in and out of play mode. I don't see much of a reason to actually serialize (as in "between Unity sessions") a delegate, I simply initialize it with some method when applicable - but I might be wrong and you could come up with some interesting example.
     
  17. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830

    1) Wow... took you a while to respond!

    2) Are you sure the ?? doesn't work? I could understand if UT's stupid 'Object' equality overrides were stuffing something up - but not simple delegates.
     
    Last edited: Mar 18, 2013
  18. jedy

    jedy

    Joined:
    Aug 1, 2010
    Posts:
    579
    I have a suspicion.

    Unity fails massively with some of the C# features in some of the builtin methods called from the native core.

    Let me explain - some stuff per say, cause the engine to crash, or misbehave in various and arbitrary ways without no other reason.
    For example I've encountered a lot of problems with default values in the Update() method.
    At some point I had an Update method which had a rather indirect call to a function with a default parameter - it resulted a failed compilation of the whole file. Unity just decided that it does not exist.
    Another example was even simpler - a direct call to a method with a default parameter ( 1 exactly ) in an Update() resulted a crash with some internal error that hadn't to do anything with the actual problem. ( I haven't done extensive testing on the subject )

    I've encountered numerous problems with the ?? operator. I suspect that they get called by the Native core and that causes the errors.

    I've been using a parenthesis trick up till couple of weeks but that ended causing problems, too.

    Basically the only bullet-proof solution is using the :? instead of the ?? operator.
     
  19. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    jedy, I don't think you're right about the null-coalescing operator. I have no troubles with it.

    MonoBehaviours can't use optional parameters, if that's what you're talking about.
     
  20. jedy

    jedy

    Joined:
    Aug 1, 2010
    Posts:
    579
    No idea mate, just sharing some personal experience.

    And still it worked with me most of the time ( almost exclusively in properties ). And at times some of them just decided that working isn't an option anymore. That came after work on some code that wasn't even closely relevant to the class which fired the error, and some heavy debugging sessions followed, but to no avail.

    PS : If my memory serves me well - some of the MonoBehaviour component property overrides ( transform, collider etc ) did that after couple of weeks using them with the null-coalescing operator.
     
  21. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,631
    Since delegates can't be serialized, how would you go about doing something like this? In this example, I want to create the delegate once in the constructor and keep it around so I can reuse it instead of having to create it every time I need it. Problem is, when a field is not serialized and Unity does an asset import operation while the editor is in play mode, the field will get wiped. (This is important for editor tools because you want them to work through an asset import.) My class isn't a Monobehaviour, so no OnEnable() to run after the asset import. The best thing I can think of is to check if myCallback == null every time I use it and then recreate it. But I'd rather have a cleaner way...

    Code (csharp):
    1. [System.Serializable]
    2. public class MyClass {
    3.     [SerializeField] private System.Action<string, object> myCallback;
    4.  
    5.     public MyClass() {
    6.         myCallback = MyCallback;
    7.     }
    8.    
    9.     private void MyCallback(string message, object obj) {
    10.         // Code here
    11.     }
    12. }
     
    Last edited: Nov 25, 2013
  22. jedy

    jedy

    Joined:
    Aug 1, 2010
    Posts:
    579
    Personally I use an auto initializing properties if I decide to stick to delegates. Yet I rarely do so, because editor code looks and functions a lot cleaner if it is fully serializable.

    Code (csharp):
    1. [System.Serializable]
    2. public class MyClass {
    3.     [SerializeField] private System.Action<string, object> myCallback;
    4.  
    5.     public System.Action MyCallback {
    6.          get {
    7.              if(myCallback == null) myCallback = Value;
    8.              return myCallback;
    9.          }
    10.     }
    11.  
    12. }
    [/QUOTE]
     
  23. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey all - Thought I'd share with you my solution to serializing and inspecting delegates via the inspector: uFAction!
    I really think there should be a powerful built-in solution for delegates and events.
     
    Last edited: May 29, 2014