C# delegates, I love you ...

Discussion in 'Unity Gossip' started by n0mad, Sep 7, 2012.

  1. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    For confirmed devs, it can be obvious, but for people like me who come from more "simplistic" languages (javascript, ActionScript, Java, etc), their true use discovery just made me cheer like a soccer fan during finals, and say ... "goddamn it, why didn't I know this earlier ?"

    So I wanted to share the experience, so other people who learn C# will not make the same mistake as me :)
    (I'm not claiming to know everything about delegates, so if this post need corrections, feel free)

    In short, delegates. That is the basic.
    You can read the page, but to sum it up, delegate is attributing a method execution to a variable at runtime. So in short, imagine you have a huge core function, SpawnEnemy(), where at some point you want to spawn very different enemies with very different behaviours, under very different conditions. Instead of writing a 5000 lines long if () {} else if () {} else if () {} crap sauce, you just insert a delegate (let's say, OnSpawnEnemy()). Then for each new SpawnEnemy() called, you can reattribute the OnSpawnEnemy delegate to a different function from another script, depending on which condition is met (indeed, OnSpawEnemy reattribution has to happen before its execution).
    For example, at time 25f, some manager reattributes OnSpawnEnemy = CreateTrolls to create Trolls, at time 50f OnSpawnEnemy = CreateOrcs to create Orcs, etc,. Then whenever I call OnSpawnEnemy(), it will create the defined type of enemy.

    But I found this MSDN tutorial page wasn't doing much justice to their real power ...

    Two things I found which were totally awesome :

    1) Anonymous delegates : I don't know if it's some coder porn syndrome, but I love this one. In short, instead of hardwriting tons of different separate functions for each delegate case you want, you just write OnSpawnEnemy = delegate { //stuffToDo } . It's a must for structure visibility. The only downside is that what's inside the delegate doesn't save conditional variables. For example : for (int i = 0; i < 5; i++) OnSpawnEnemy = delegate { Debug.Log(i); } will always display 4. So you have to hardwrite variables. But it's not a big deal really, as you're not supposed to create super complicated anonymous delegate bodies (for debugging's sake).
    What I like with anonymous delegates is that you can "hack" a core behaviour at any given time under whatever condition you want, just by writing a quick subscript. Visually, the fact of not declaring a whole new function will 100% fit the very contextual nature of the change, keeping your core architecture clean.

    2) the most important one : the += and -= operators. So let's take our OnSpawnEnemy(). What if at some time, I want to spawn 4 Trolls AND 4 Orcs ? You just write :
    Code (csharp):
    1. OnSpawnEnemy += CreateTrolls;
    2. OnSpawnEnemy += CreateOrcs;
    3. OnSpawnEnemy(4);
    And if later on, you don't want to spawn Orcs anymore, just write :
    Code (csharp):
    1. OnSpawnEnemy -= CreateOrcs;
    As said in 1), the real power here is to make you able to hack a generic, core function. When you have a highly complex architecture, it's always better to merge redundant operations into core functions, so you trim the code noise for better clarity. It's better for debugging, too. For example with Orcs and Trolls, instead of creating 2 separate full creation functions, you merge every critical common code into SpawnEnemy (like memory management, common asset instancing, common variables like HP, strength etc declarations, etc). And then you create the "exoticisation" of your enemy in specific, separate functions that you integrate in the core one, like written above.
    At first, I was using virtual/override methods to fit each different injection. But what if I wanted to create 2 merged overrides within the same virtual entry ? I had to create another virtual into the first virtual, or one after another, etc .... VIRTUALCEPTION ! a nightmare :) And most of all, a huge waste of code space as you have to declare your empty virtual functions ...
    But here with delegates, no need to declare useless virtual empty functions.. just to call OnSpawnEnemy() (+test if null beforehand), and add/retrieve as many hacks as I want with a single += operator, from wherever I want.

    ________________

    Conclusion : I don't know if delegates were created (or function pointers in C) with videogames in mind, but it's clearly a must for behaviour management, and totally fits the nature of videogames code dynamic logic.
    Want to add a powerup to that punch ? No problem. OnStrikePunch += PowerUp;
    Want to retrieve that wall bounce testing for that ball ? No problem. OnBallHitTest -= TestWalls;

    I can also see how powerful delegates can be in a collaborative environment. The architect creates core code "waypoints" as delegates, and then each dev can control what type of behaviour he wants to add inside that waypoint.


    Anyway, just to wanted to share my nerdiness for delegates, but feel free to add anything you want about them, like other particularly useful scenarios, etc.
    Oh and did I mention that delegate were fast ? ;-)
    (That was my first fear when I realized how powerful they were)
    Last edited: Sep 7, 2012
    rakkarage likes this.
  2. npsf3000

    npsf3000

    Member

    Joined:
    Sep 19, 2010
    Messages:
    3,747
    Now read up on Lambda's, Action Func.
    rakkarage likes this.
  3. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    But I'm only lvl 33 .. :p
    Thanks ;-) C# is really feeling like an endless Ali Baba's cave for efficiency, stability AND simplicity. Why isn't it more widespread in videogame dev jobs ?
    (granted I didn't really extensively test C++, but it seems like C# is evolving much faster)
    Last edited: Sep 7, 2012
  4. Metron

    Metron

    Member

    Joined:
    Aug 24, 2009
    Messages:
    703
    That's one of the reasons I completely switched to C# within Unity. I wish I could switch to C# in my day to day work, too :)
  5. Tim C

    Tim C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Messages:
    1,457
    Now lets talk about the bad side of delegates.... They play havoc with the GC unless you know what you are doing. Lets go through 2 specific use cases:

    1) You add a delegate that references something large in memory (Texture2D for example). You hook this delegate up to an event and then it's all good. Nice callbacks and everything. Now for some reason the container for the delegate goes away (the game object holding the Texture2D). You have never removed the reference to the delegate on the event. The Texture2D is still referenced. It will stay around until the class holding the event goes away. As it's very common for these classes to be 'Managers' of some type it's likely that your Texture will never be GC'd :(

    2) You use anonymous delegates, the delegate references a Texture2D. There is NO WAY to remove the delegate from an event. You are back in situation 1.

    For the forthcoming GUI system we decided to implement 'better' delegates that work by holding weak references and play nice with the GC. They will work throughout unity and can also be serialised. They are not ready for release yet but I think they are pretty cool.

    If you are going to use c# delegates try and avoid anon delegates. Also be really careful with memory overhead in using some of c#'s 'nice' features like linq and foreach and the like. Use the profiler and make sure to keep your allocations in check.
    rakkarage likes this.
  6. tatoforever

    tatoforever

    Member

    Joined:
    Apr 16, 2009
    Messages:
    2,327
    Delegates are really powerful but use it wisely! And C# is my fav programming language! :D
  7. alexzzzz

    alexzzzz

    Member

    Joined:
    Nov 20, 2010
    Messages:
    809
    Code (csharp):
    1.  
    2. for (int i = 0; i < 5; i++)
    3.     OnSpawnEnemy = delegate { Debug.Log(i); }
    4.  
    This code makes no sense, at least I don't see any, but if you want to get 01234, you can write it like this:

    Code (csharp):
    1.  
    2. for (int i = 0; i < 5; i++)
    3. {
    4.     int i2 = i;
    5.     OnSpawnEnemy += delegate { Debug.Log(i2); };
    6. }
    7. OnSpawnEnemy(); // prints 0 1 2 3 4
    8.  
    The temporary local variable is necessary, otherwise you'll get 55555 or maybe something else you don't expect too see. If you wonder why, read about closures and what they are close over.

    ---
    If at some point you decide to subtract your delegates, be careful:

    Code (csharp):
    1.  
    2. class Program
    3. {
    4.     static void Main(string[] args)
    5.     {
    6.         Action a = A;
    7.         Action b = B;
    8.         Action c = C;
    9.  
    10.         Action x = (a + b + c) - a - c;
    11.         x(); // prints B
    12.         Console.WriteLine();
    13.  
    14.         Action y = (a + b + c) - (a + c);
    15.         y(); // still prints ABC, because ABC sequence doesn't contain AC
    16.         Console.WriteLine();
    17.  
    18.         Console.ReadLine();
    19.     }
    20.  
    21.     private static void A() { Console.Write('A'); }
    22.     private static void B() { Console.Write('B'); }
    23.     private static void C() { Console.Write('C'); }
    24. }
    25.  
  8. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    @Stramit : Thanks for the infos ! There's clearly a danger here ... I should check that out now ... :)

    @Alexzzzz : the code was just for quick explanation purpose ;-) And thanks for your tips too ! I didn't grab that boolean logic yet, that's great info.

    So in the end, should I deduce that Virtual/Override are safer concerning memory and GC ?
    Therefore better for an intensive function triggering ? (like state manager)
    Last edited: Sep 7, 2012
  9. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    @Stramit :
    Now I'm afraid :p
    If I understood correctly, you mention there that when a container (gameobject) is destroyed, the references in his components are not ? (assuming they're not referenced anywhere else)
    Is it like that ? For example I have let's say a "system" gameObject, where I put a bunch of Texture2D references, all loaded with Resources.Load. If I destroy this System gameObject, those Texture2D references won't be cleared ? :O
    Shouldn't it be the case ?
  10. Tim C

    Tim C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Messages:
    1,457
    Well the references will be GC'd so long as nothing else is referencing them. The thing is by adding delegates the delegate can reference the fields and they will not get GC'd.
  11. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Ok thanks :)
  12. alexzzzz

    alexzzzz

    Member

    Joined:
    Nov 20, 2010
    Messages:
    809
    A brief example:

    Code (csharp):
    1. public class FooManager : MonoBehaviour
    2. {
    3.     public Action actions = null;
    4.  
    5.     public void DoActions()
    6.     {
    7.         actions();
    8.     }
    9.  
    10.     // ...
    11. }
    12.  
    Code (csharp):
    1.  
    2. public class Foo : MonoBehaviour
    3. {
    4.     private byte[] hugeAmountOfData = new byte[100000000];
    5.     private FooManager manager;
    6.  
    7.     private void Start()
    8.     {
    9.         manager = (FooManager) FindObjectOfType(typeof (FooManager));
    10.         manager.actions += DoSomethingUseful;
    11.     }
    12.  
    13.     private void DoSomethingUseful()
    14.     {
    15.         //...
    16.     }
    17.  
    18.     // ...
    19.  
    20.     private void OnDestroy()
    21.     {
    22.         manager.actions -= DoSomethingUseful; /* You need this, because 'actions' delegate
    23.                                               * still contains a reference to this Foo instance,
    24.                                               * which means the instance will always stay alive
    25.                                               * and won't be collected by the GC. */
    26.     }
    27. }
    28.  
    rakkarage likes this.
  13. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Ok. I understand the logic, but, shouldn't a Destroy() (in an ideal world) turn every instance of it to null?
    Like here, calling manager.actions() should ideally make a NullReferenceException ? (if we don't substract to manager's delegate)
  14. alexzzzz

    alexzzzz

    Member

    Joined:
    Nov 20, 2010
    Messages:
    809
    Code (csharp):
    1.  
    2. var referenceToInstance = new GameObject("Lucky");
    3. var secondReferenceToTheInstance = referenceToInstance;
    4. var andAnotherOne = referenceToInstance;
    5.  
    6. DestroyImmediate(referenceToInstance); /* Releases some internal resources that
    7.     the instance were using, but it doesn't destroy/erase/eliminate the instance
    8.     * completely, only the garbage collector can do it. But at this moment the GC
    9.     * still can't collect the object, because there are three references to it.
    10.     * There's no any magical way that Destroy() could set all the variables to null,
    11.     * they are still pointing at the halfdead object. */
    12.  
    13. Debug.Log(referenceToInstance == null); /* Prints 'true', because UnityEngine.Object
    14. * overrides the comparison and returns 'true' if the object is marked as destroyed.
    15. * It doesn't mean that the reference is actually null. */
    16. Debug.Log(ReferenceEquals(referenceToInstance, null)); // Prints 'false' (that's the truth :)
    17.  
    18. referenceToInstance = null;
    19. secondReferenceToTheInstance = null;
    20. andAnotherOne = null;
    21.  
    22. /* Now there are no live references to the object and the GC is able to collect it. */
    23.  
    24. Debug.Log(ReferenceEquals(referenceToInstance, null)); // Prints 'true'
    25.  
    rakkarage likes this.
  15. HarvesteR

    HarvesteR

    Member

    Joined:
    May 22, 2009
    Messages:
    426
    Gotta love C# :)

    I didn't know about Action and Func. I've been writing my own delegate definitions here, and looks like I may have reinvented the wheel a few times, heh.

    Cheers
  16. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Ok thank you Alex, that makes sense now.
    If we wanted Destroy to take full control of that nullification, there should be a "ref" keyword as object parameter I suppose.
    Last edited: Sep 7, 2012
  17. Cripple

    Cripple

    Member

    Joined:
    Aug 16, 2012
    Messages:
    36
    When you do a += to register a delegate you must always add a -= to the same delegate otherwise the game will leak like hell.

    By the way delegate are not that fast, they are slow actually. But indeed it is very powerful and useful for event management.

    You made a example with Spawning enemies, but instead of using delegate, i prefere here to use an Interface (with a spawn function) and to implement decorator pattern to add different logic to the spawn.

    http://en.wikipedia.org/wiki/Decorator_pattern
  18. Foam

    Foam

    Member

    Joined:
    Jun 13, 2012
    Messages:
    322
    Welcome to continuations/closures. ;)

    BTW JavaScript supports this and, while I haven't used Java in 15 years, I would assume it does too though it might be hackish, I'm not really sure. In fact most languages support this sort of thing though it might take a bit of thinking-outside-the-box to get it done.
  19. npsf3000

    npsf3000

    Member

    Joined:
    Sep 19, 2010
    Messages:
    3,747
    Thought someone would find them useful. C# started off with hideously verbose delegates and slowly made them more and more usable.

    For those interested in finding out more about C#'s awesomeness C# in Depth is a great book. My list of everyday cool things are:

    • Extention Methods
    • Lambda's
    • Linq
    • IEnumerator/IEnumerable
    • Named Optional paramaters. Oh, and Params.
    • Nullables
    • Generics

    I don't declare delegates often, but I do like mucking around with them on occasion:

    Code (csharp):
    1.  
    2. delegate _ _();
    3. _ _ = null; _ = () => _;
    4.  
    5. _()()()()()()()()()()()()()()()()()();
    6.  
  20. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
  21. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    I spent the week end converting all my virtual/overriden core methods to delegate, so I now got a clear look about the difference (saved 2 different repositories so I can compare). As an information, there were a lot of override calls in my system mechanics.
    And it seems I gained 0.1 - 0.2ms frametime (which is a lot, as I was at 1.0ms already, so it's 10%-20% increase).

    I don't really have the time to do proper metrics, so yeah it's kind of statistics put out of my ass right now. But I feel it's very slightly faster. It might also have to do with having a few enums to test with override method that I don't have to test now, but enums are supposed to be cheap, so ..
    Anyway the big gain is that now I can add pretty every kind of new behaviour into just every input of my state machine, which is huge improvement :)
    (think "very specific combat rules", "complex conditional behaviours", "extensions to core fight mechanics", etc)

    This was worth the 2 days of work.
    Last edited: Sep 10, 2012
  22. WillBellJr

    WillBellJr

    Member

    Joined:
    Apr 10, 2009
    Messages:
    397
    Delegates and especially Events are the reason while I dropped C++ like a hot potato when C# first became available - I LOVE C#, haven't programmed in C++ since.


    The fact that Unity supported C# was a tick off the FIRST checklist item on my game engine list!


    -Will
  23. holyjewsus

    holyjewsus

    Member

    Joined:
    Mar 7, 2011
    Messages:
    587
    do you think you can share a small sample of the way these functions actually work together, I understand delegates on a theoretical level but havn't seen any good REAL examples.
  24. AmazingRuss

    AmazingRuss

    Member

    Joined:
    May 25, 2008
    Messages:
    865
    set the event = null, and all the delegates go away.
  25. sotirosn

    sotirosn

    Member

    Joined:
    Jan 5, 2013
    Messages:
    22
    Currently I am developing an editor tool so you can assign delegate members from the inspector.

    So basically if you have this..

    Code (csharp):
    1. [System.Serializable]
    2. public delegate IEnumeartor Routine();
    3.  
    4. public class Mono : MonoBehaviour {
    5.     public Routine onStart, onExit;
    6.  
    7.     public IEnumeartor R1() { ... }
    8.     public IEnumeartor R2() { ... }
    9.     public IEnumeartor R3() { ... }
    10.  
    11.      void IEnumeartor Start() {
    12.          yield return StartCoroutine(onStart);
    13.          yield return StartCoroutine(onExit);
    14.      }
    15. }
    16.  
    Then a drop down with [R1, R2, R3] will be available for onStart and onExit fields. It works amazingly well and I am going to write a visual scriptor like PlayMaker but with delegates in place of Action objects.
    Last edited: Jun 14, 2013
  26. _Daniel_

    _Daniel_

    Member

    Joined:
    Feb 28, 2007
    Messages:
    1,871
    cool, but just out of curiosity, any reason you made your delegate an IEnumerator? Do you have void working too?
  27. angrypenguin

    angrypenguin

    Member

    Joined:
    Dec 29, 2011
    Messages:
    5,193
    Were you doing this just for speed?

    It's not the kind of thing I'd do, because while both can often be used to implement the same functionality in different ways, design wise they're intended for different things and I'd rather not fight against my tools.
  28. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    At first it was for speed :)
    But soon I realized the superior convenience of delegate composing ! Being able to add / substract a whole chain of behaviors on-the-fly is incredibly fit to video games mechanics. It's an incredible tool.

    Sounds powerful !
  29. ronan.thibaudau

    ronan.thibaudau

    Member

    Joined:
    Jun 29, 2012
    Messages:
    1,734
    That's a really bad call for delegates imho, it's well documented and saying "we'll make something else so users don't shoot themselves in the foot" means you're actually making it likely anyone who knows C# will shoot itself in the foot.
    Also begginers should swarm their programs with LINQ and foreach, and at equal level, people won't produce faster code without linq that with it, quite the other way around, same for foreach except for the bad GC, but it's certainly not to take as a rule of thumb, people should use foreach everytime they actually semantically want to foreach, and then optimise later when (if!) needed. My take on this is:
    - Either you're begginer level and you should use foreach
    - Or you're advanced enough to care and you should still use foreach, wrapping the enumerator so that it doesn't leak !-_-! :)
  30. lacost

    lacost

    Member

    Joined:
    May 30, 2012
    Messages:
    780
    Fun thing about delegates.
    Few days ago I got feedback from one of my customers, he describe why he choose my system insted standart delegate. (I will not post a url, because advertising is not purpose of this post)

    So here is his problem:

    All code in dll. When he uses delegates from dll, everything working in editor and IOS simulator. But delegates not working on devise.
    Every time when hi try to dispatch event hi got:
    Code (csharp):
    1.  
    2. ExecutionEngineException: Attempting to JIT compile method '(wrapper managed-to-native) System.Threading.Interlocked:CompareExchange
    3.  
    Point is that when you have code inside dll like
    Code (csharp):
    1.  OnMouseUpClick += method;  
    You will get error when you will try to dispatch OnMouseUpClick;

    Everything great if you use:
    Code (csharp):
    1.  OnMouseUpClick = method;  
    And my system also work well because I use HashTable to store delegates.

    But it just funny, that
    Code (csharp):
    1.  OnMouseUpClick += method;  
    will not work on IOS if it's inside dll.
  31. ronan.thibaudau

    ronan.thibaudau

    Member

    Joined:
    Jun 29, 2012
    Messages:
    1,734
    This sounds like a bug unless you're somehow instantiating a non multicast delegate, but by the name it sounds like an event so that shouldn't be the case?
  32. lacost

    lacost

    Member

    Joined:
    May 30, 2012
    Messages:
    780
    Here is full code listing. And it's giving exception on event dispatch, tested on (iPhone4, iPhone4s, IPad2, IPad mini)


    This is "DelegateButton.cs" - with will work from dll
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class DelegateButton : MonoBehaviour {
    6.  
    7.     public delegate void MouseUpClickHeadler();
    8.     public event MouseUpClickHeadler OnMouseUpClick;
    9.    
    10.     public float w = 150;
    11.     public float h = 50;
    12.    
    13.     void OnGUI() {
    14.         Rect buttonRect =  new Rect((Screen.width - w) / 2, (Screen.height - h) / 2 - 150, w, h);
    15.         if(GUI.Button(buttonRect, "click me delegates")) {
    16.  
    17.             if(OnMouseUpClick != null) {
    18.                 OnMouseUpClick();
    19.             }
    20.         }
    21.     }
    22.  
    23.  
    24. }
    25.  
    And this is "ExampleListner.cs" witch should be simply in our Asset folder and attached to a Main Camera in a empty scene

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ExampleListner : MonoBehaviour {
    6.    
    7.     public GUIStyle style;
    8.    
    9.     private string label = "Click's: ";
    10.     private int count = 0;
    11.    
    12.  
    13.     void Start () {
    14.  
    15.         DelegateButton db = gameObject.AddComponent<DelegateButton>();
    16.         db.OnMouseUpClick += onButtonClick;
    17.     }
    18.    
    19.    
    20.     private void onButtonClick() {
    21.         count++;
    22.     }
    23.  
    24.    
    25.     void OnGUI() {
    26.         GUI.Label(new Rect(0, 0, 200, 100), label + count.ToString(), style);
    27.     }
    28.    
    29. }
    30.  
    31.  

    This is defiantly bug.
    But it is very difficult to say what is the cause) Unity? IOS? AOT compiler?
    The exception will appear only on device. (In IOS Simulator or Unity Editor this code will work just fine)
    Last edited: Jun 14, 2013
  33. jc_lvngstn

    jc_lvngstn

    Member

    Joined:
    Jul 19, 2006
    Messages:
    1,311
    C# is an awesome language. I chalk it up to a couple of things:

    1. Snobbery. I used to work with a C++ guy, and as far as he was concerned, if you weren't using C++, you were a kid pretending to program with a kiddie programming language. I think this is very common (to lesser degrees).
    2. MS bandwagon hate. Granted, lately MS seems to be doing everything they can to make themselves look rotten. But their development tools are the best, imo. Their OS...is there a more versatile OS on the planet, that supports the amount of hardware and software thrown at it? Not to mention, it is THE biggest gaming platform ever.
    3. Ignorance :)
  34. Asvarduil

    Asvarduil

    Member

    Joined:
    Nov 19, 2009
    Messages:
    2,600
    Note: In my previous job, I happened upon the following material by accident. When I asked my superiors about it, they knew nothing themselves, so I wound up creating a presentation on it. My knowledge since then has matured, as I've used it a bit more in my own game projects, so the material in this post will be a bit better than that presentation. But, it's still a wall of text. Worse, there is no TL;DR version. So, if text scares you, either A) don't read it, or B) don't read it all at once.

    Time for you to level up again Mr. Lv.33 Developer!

    LINQ/Lambda expressions are the next step upwards from this point.

    Code (csharp):
    1. using System.Linq;
    2.  
    3. // ...Boilerplate here...
    4.  
    5. public long Fibonacci(long x)
    6. {
    7.   // Note: this is the version compatible with Visual Studio.  In Mono, you can combine this into a single line.
    8.   Func<long, long> fib = null;
    9.   fib = n => (n < 2) ? n : fib(n-2) + fib(n-1);
    10.  
    11.   return fib(x);
    12. }
    This single method finds the fibonacci number for a given index, using a function delegate. In .NET 3, two additional 'delegate' types were introduced: Actions, which act similarly to SUBs in BASIC languages (e.g. a function with return type of void), as such:

    Code (csharp):
    1. Action<int, int> foo = (a, b) => Debug.Log("Sum: " + (a + b));
    The generics for an action denote the types of the arguments.

    Similarly, Funcs are similar to the BASIC FUNCTIONs - they return some result, as seen above. In the case of a Func, the final type given is the output type; all preceding types are argument types:

    Code (csharp):
    1. Func<int, int, int> foo = (a, b) => a + b;
    This is important for when you are using LINQ, which is a framework that provides brief ways of working with IEnumerable types. While it is little more than syntactical sugar, it helps. Imagine you have a foreach as such:

    Code (csharp):
    1. SomeBehavior desiredBehavior = null;
    2. SomeBehavior[] behaviors = GetComponents<SomeBehavior>();
    3. foreach(SomeBehavior behavior in behaviors)
    4. {
    5.   if(behavior.SomeProperty == true)
    6.   {
    7.     desiredBehavior = behavior;
    8.     break;
    9.   }
    10. }
    11.  
    12. return desiredBehavior;
    In that code, we're looking for a behavior in a series of behaviors with some property that matches a desired value. But, look at all that ugly code! LINQ gives us a better way to write that:

    Code (csharp):
    1. SomeBehavior[] behaviors = GetComponents<SomeBehavior>();
    2. SomeBehavior desiredBehavior = behaviors.FirstOrDefault(b => b.SomeProperty == true);
    3. return desiredBehavior;
    Note: You see the .FirstOrDefault() method call? Don't check for null! The reason is that under certain circumstances, not everything is by default null! You would perform a check against default([Your Type Here]) to determine if a default object was returned.

    The way most LINQ methods work, is they conceal a foreach loop against an IEnumerable. The lambda expression is used as a filtering condition. Thus, while behind the scenes that nasty foreach is still happening, it's not cluttering up your code.

    I highly recommend researching this aspect of C#, especially for your Unity creations; recently, I was able to use LINQ and the .NET 3 delegate types to build a pretty nice state machine I'm using for AI in my current project, in less than 100 lines of code (much of that being language boilerplate.) With learning delegates you took your first step into a larger world - now, keep going!

    Note: Given that C# memory leaks were brought up earlier in the topic, these delegate types behave like other delegates if bound to events - you must clean them up to prevent a memory leak. The Actions and Funcs are nice because you can declare what amounts to a temporary function that gets GC'd when a function goes out of scope, unless the function is being returned, as in the scenario of creating a C# closure:

    Code (csharp):
    1. public Action MyClosure(int parameter)
    2. {
    3.   int someBoundary = parameter - 1;
    4.   int anotherBoundary = parameter + 1;
    5.  
    6.   return () => {
    7.     Debug.Log(String.Format("Parameters: [{0},{1},{2}]", someBoundary, parameter, anotherBoundary));
    8.     _somePrivateField += parameter;
    9.   };
    10. }
    The Action returned will keep the reference to someBoundary and anotherBoundary around until the action is no longer referenced, and thus GC'd. Just another cool, but advanced, and rather uncommon thing you can do with the .NET 3 delegate types.
    Last edited: Jun 14, 2013
  35. frank28

    frank28

    Member

    Joined:
    Dec 30, 2013
    Messages:
    2
    Hey, come back to check C++11, which includes function/bind in std now:)
  36. Smooth P

    Smooth P

    Member

    Joined:
    Sep 15, 2012
    Messages:
    214
    This has nothing to do with delegates specifically... what you've described is the bad side of being a bad programmer. You can hold references in any number of ways.

    And I love this thread. The entire Unity API would be a million times better if it used delegates and events instead being designed for people who for some reason are writing code but can't handle the += and -= operators and dirtying / race-conditioning everything up with all the handholding of the completely misnamed reflection "callbacks".

    Edit: Whoa, necro. Still a good thread. :)

    So, umm... it's been 17 months and there's still no sign of the "artists writing bad code friendly" event system. Maybe you guys should just focus on clean, allocation free programming APIs designed for programmers instead of artists.
    Last edited: Feb 10, 2014
  37. ronan.thibaudau

    ronan.thibaudau

    Member

    Joined:
    Jun 29, 2012
    Messages:
    1,734
    Doesn't need to use events and the registering / unregistering syntax, can just use raw delegates and have them passed into methods for subscribtion have unity manage them so bad end user isn't even an excuse.
  38. actuallystarky

    actuallystarky

    Member

    Joined:
    Jul 12, 2010
    Messages:
    156
    I cannot agree more.

    I have worked in the games industry for about 15 years now. 13 of those years was spent as an artist in various senior roles. When I started my own games company I had to learn how to code from scratch. Even though I'm a great example of the artist-turned-coder I can't stand UT's attempts to "baby" beginner coders by building inefficient systems that encourage bad practices.

    The best way to help an artist learn how to code is providing an API that encourages and rewards good code.
  39. ronan.thibaudau

    ronan.thibaudau

    Member

    Joined:
    Jun 29, 2012
    Messages:
    1,734
    What's too bad is that pretty much all of .net is well designed and easy to use, so since you kinda have to learn .net APIs anyway to be proficient in unity it doesn't even saveup learning time it's just a bad tradeoff, you give away something get nothing in return.
  40. angrypenguin

    angrypenguin

    Member

    Joined:
    Dec 29, 2011
    Messages:
    5,193
    Well, you should learn some .NET, but plenty of people don't even realise it's there to be learned about.

    Check out the scripting section, for instance, and see the almost weekly threads about saving complex data using PlayerPrefs instead of using any of the many options available to you through the .NET/Mono standard library. People tell me they avoid it because it doesn't work reliably cross-platform, but while it does have its niggles if that were the case generally speaking then most of the code I wrote wouldn't work most of the time, so I really don't buy it. (And all of the niggles I've found were in places that, understanding how computers/operating systems work, it was pretty obvious for me to think "I should check if this works differently on a mobile device".)

    I'm also no fan of the automatic reflection-based callbacks. And I'd love events (or something) for the things that I sometimes find myself polling and yet which I expect to change only rarely... it always makes me feel dirty. Though in some cases adding the event in the back-end might make performance suffer far more than my polling does. (Eg: I'd love to have an event on transforms that tells me when they're modified or that they became dirty some time in the current frame, but I understand that could potentially trash internal optimisations.)
    Last edited: Feb 11, 2014
  41. MrProfessorTroll

    MrProfessorTroll

    New Member

    Joined:
    Sep 1, 2013
    Messages:
    383
    I switched from C++ to C#! Im having so much fun with it! Im not gonna try UnityScript because I feel like it isn't as fun. The way you declare variables bother me so much! I cannot stand the 'var' type! I CANT! Its not so hard to just put in a specific type. It bothers me so much and having to declare variables like 'var something : GameObject' is annoying to me.
  42. Smooth P

    Smooth P

    Member

    Joined:
    Sep 15, 2012
    Messages:
    214
    You'll come back to loving var... with type inference.

    At this point I can't stand specifying types (and generic parameters) for fields...

    Of course, I use a helluvalot of generics.
    Last edited: Feb 11, 2014
  43. MrProfessorTroll

    MrProfessorTroll

    New Member

    Joined:
    Sep 1, 2013
    Messages:
    383
    It's not good practice :(
  44. jmatthews

    jmatthews

    Member

    Joined:
    Jul 27, 2011
    Messages:
    187
    The common idiom is to use var where the type can be inferred from the remainder of the code line.

    e.g

    var _myController = GetComponent<CharacterController>();

    Which is a common use case and doesn't hurt readability. One could argue it helps readability because it is less verbose. It also helps to make sure your variable names are well thought out. For that you want to be more verbose than most people tend to be.

    At least that's been my experience.
    Last edited: Feb 11, 2014
  45. ronan.thibaudau

    ronan.thibaudau

    Member

    Joined:
    Jun 29, 2012
    Messages:
    1,734
    It's very good practice, don't come from another language with another mindset try to apply it to a new language you learn. In C#, it's perfectly fine practice, var is as strongly typed as specifying the type, and much more readable.
    rakkarage likes this.