Using Delegates, Func and Lambdas: A Tutorial With Soldiers
In this tutorial, written for beginning programmers, I’d like to show a little demonstration on the usage of delegates and how we can go all crazy by refactoring and magically see all our duplicate code disappear.
Imagine we are writing the next ultimate Command&Conquer spinoff which can run on any computer …in console-mode. Now imagine that in the early phases of our prototype we create a Soldier class: all soldiers should be able to simply “Do their thing” when asked to do so. When they do, they should report on what they do, of course. Typical “DoYourThing” actions might include throwing a grenade, cowardly running away or going into Full Rambo-mode:
Phase 1: Oldschool coding
Smart as a cookie , we create our Soldier class:
Look how smart. We made our DoYourThing() method virtual, allowing us to create an over-the-top amount of Soldier childclasses that all override the method with the specific action that Soldier can do.
Mmm…but all these inherited classes (GrenadeSoldier, RamboSoldier, etc.) are blowing out of proportion and contain 99% duplicate code.
Phase 1.2 or 1.1
Depending on your train of thought, you might first have done this step or first the previous one and then this one. Anyhow, let’s be a smarter cookie and don’t go for inheritance and simply write a switch:
Again, we’ll ignore the obvious improvement of making this ActionNumber into an enum, but as you might have guessed, this still ain’t the right way.
Phase 2: On with the delegates
Since this tutorial about moving methods around like objects, let’s try to refactor our code so that delegates start showing their worth.
It’d be much handier if we could keep the implementation of the different actions a soldier can do outside the Soldier class itself. Imagine that in a future update of your game you’d like to add new cool soldiers and/or their actions, if we keep the everything tightly bound (i.e. keep the actions inside the soldier) we’d have to recompile our entire Soldier class. So, out with the Soldier actions, in with delegates!
So we defined a new SoldierAction delegate which we’ll use as our vehicle to ‘give’ the action-implementation to the soldier.Notice that we basically changed the type of our ActionNumber from int to the newly creates SoldierAction delegate type (and gave it a slightly improved name).
Imagine now that we moved the different actions to self-contained methods , outside the Soldier class:
We now can create cute little Rambo’s and other types that will d the actions they were made for:
Notice that IntelliSense (or is it Resharper?) is kind enough to point out that we could also write:
For all you lambda lovers out there: suppose every action will be totally different and writing a method for each action would be overhead, we ould also write:
Phase 3: Enter Action<T>
In the previous refactor, we explicitly defined a new delegatetype:
Thanks to the more generic Func/Action delegates, we can remove that line in it’s entirety. Basically Func and Action are generic, parameterized delegates. Their main reason of existence is simply to remove lines like the one just mentioned in which we explicitly define a new delegate.
- Func delegates are used to encapsulate methods that return a value.
- Action delegates are used to encapsulate method that return void.
Now, being the lazy bum that I am: check out this excellent tutorial on the basics of both Func and Action delegates. I can’t explain it any better than they do, so hop to that site, read it all and come back here. I can wait.
Since our soldier actions return void, we’ll go for the Action<T> delegate. However, because no parameters are provided we can use the non-generic one. Our refactored class becomes (and we delete the SoldierAction delegate definition):
Note that we don’t have to change anything in the way we need to instantiate new Soldier objects.
Phase 4: Icing on the cake
With this done. We can now create vast armies of soldiers doing their thing:
Immediately our duplicate code alarm goes into overdrive. Now what? Well, just for the sake of completeness. Let’s try to make those 3 lines in which we add new soldiers shorter, because, be honest, they are 90% identical (don’t start calculating this percentage please).
Let’s wrap the ‘action’ of creating new soldiers and putting them in our list into…you’ll never guess it….Wait for it. ..Yup, let’s wrap it into an Action delegate! And for the sake of being ubercool (hey we are writing C&C after all) , we’ll add in some samba..I mean lambda flavor!
Now, to be honest. This last example was mainly introduced to show how we can define generic delegates: we simply state what parameters the delegatemethods accept (string, Action in our case) and we’re done; no need to define a new delegatetype first.
How could we use our new fastSoldierMaker delegate? Imagine we have several Barracks, each which creates a specific type of soldier. We could pass the fastSoldierMaker to each barrack when it is constructed: each created soldier will automatically be added to our full list soldiers:
I leave it up to you to create a nifty random name generator for each soldier.
Next we can now do something like:
So, that’s it for now. Hope you’ve felt a bit of ‘the force’ of using delegates and their more modern generic counterparts.