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 :
-
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. -
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 :
OnSpawnEnemy += CreateTrolls;
OnSpawnEnemy += CreateOrcs;
OnSpawnEnemy(4);
And if later on, you don’t want to spawn Orcs anymore, just write :
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)