Programming in C#, Part 7: Delegates & Lambdas

Long time no see, unless you’re binging these tutorials, then it’s been no time at all. But today we will be talking about delegates and lambdas, which may be tricky for some. Extra terms will be bolded as per usual, to signify you might be interested to google and research more on the topic.

Delegates, What Are They?

They’re awesome. Delegates I’ve found can be most easily compared to a list of functions. You can add functions to your delegate list, and then you can call them all at once. We basically can create a template of what type of function can be passed to the delegate, and all functions added to it must match that template. Then we make an instance of said template.

Making a Delegate

To do this, we use the delegate keyword to define our template; and this is very similar to how you would define a function since we’re creating a template of a function essentially. Here’s a basic example:

delegate void VoidDelegate();

You can see it’s very similar to a function, we define a return type as void, then we name it VoidDelegate, and then we have a space for parameters (in this case of course, we’re not using any.

Now we’ve created a template, and we can now make an instance of it as a variable, like we would with any other type. We simply can make VoidDelegate GamePaused; and we’ve made an instance!

Adding Functions

Now we can add functions to our delegate, and it’s pretty easy. You can simply use the =, +=, and -= to set, add to, or remove functions from an instance, respectfully. Keep in mind, if you never set a delegate to have any values, it will be null, so you’ll want to check for that.

This can be really useful for creating event systems. So let’s say we have this as an example:

public static class EventSystem
{
    public delegate void VoidDelegate();

    public static VoidDelegate PauseGame;
    public static VoidDelegate ResumeGame;

    public static void TriggerPause()
    {
        if(PauseGame != null)
            PauseGame();
    }

    public static void TriggerResume()
    {
        if(ResumeGame != null)
            ResumeGame();
    }
}

Here we’ve made a very basic EventSystem, it’s static so we can access it from anywhere (if you don’t remember what static means, check it out on my last tutorial). Now any other classes could add their own functions for pausing mechanics onto this, then if anything calls for it to pause, everyone’s functions will update.

So now if we have some other classes here for example:

public class Player
{
    bool paused = false;
    public Player()
    {
        EventSystem.PauseGame += Pause;
        EventSystem.ResumeGame += Resume;
    }

    void Pause()
    {
        paused = true;
    }

    void Resume()
    {
        paused = false;
    }

    ~Player()
    {
        EventSystem.PauseGame -= Pause;
        EventSystem.ResumeGame -= Resume;
    }
}

We have a player class here, and in our constructor we add our two functions to the delegate lists. If we make multiple instances of Player, all of their individual functions will be added. In this case we’re just turning a bool on and off, we could use this to prevent input or movement for example.

Then we also remove the functions in the deconstructor, that way we don’t leave the functions left in the delegate.

So now if we trigger it somewhere by using EventSystem.TriggerPause(); then all of the player instances will call their Pause functions and have their pause variables set to true until we call EventSystem.Resume();.

You can also pass delegates as parameters in functions which can be super useful, which is where lambdas can come into play.

Lambdas

Lambdas can be a bit tricky to understand, but they’re basically like a delegate happy meal. Instead of assigning an entire function to the delegate, you can create a snippet of code on the fly for the delegate to be assigned.

A basic lamda looks like this (parameters) => { code }, so in the parenthesis is where we define the variable parameters for the lambda based on the parameters of the delegate. Note we’re not assigning values here, we’re defining variables to use in our lambda. So if we have this delegate delegate int MyDelegate(int x), we could make a lambda to double the value with:
(int valueToDouble) => { return valueToDouble*2 };

We can assign these to delegates directly, or pass them through functions as delegates. So we could make this function:

void DoSomething(VoidDelegate actionToComplete = null)
{
    for(int i = 0; i < 100; i++)
    {
        Console.WriteLine("Value: " + (i+1));
    }
    if(actionToComplete != null)
        actionToComplete();
}

DoSomething(() => { Console.WriteLine("We finished!") });

 

Here we have it so whatever action we want is called after the function has completed; it’s an optional parameter, so if we don’t pass anything in nothing will run. We print out to our console for each value, then when that’s all done we call whatever action that was defined. Here we pass in a lambda that just writes out “We Finished!” to the console, but you could pass anything. That could pass an entirely new function or loop or anything to do.

Actions, Funcs, and Predicates

Now that you know how to create a delegate, there are some options to use the same concepts but without having to make your own template. I’m going to cover each briefly, but you should do some extra research for more information about using them. Also note each of these are under the System namespace, so you may need to access them as System.Action, System.Func and System.Predicate if you’re not using the System namespace.

All of these are basically predefined delegates with different return types, and they allow you to give parameters. They can all be used the same as a delegate, adding to their values, passing it through a function, giving it a lambda, etc.

Action<T, …>

An action is basically a delegate with a void return type, and you can optionally have up to four parameters. You can create this simply with:
System.Action<AnyParameters> myAction.
Example: System.Action<int> bob = (x) => { x++; }

Func<T, …, TResult>

A func allows you to define your return type in it’s <>, whatever the last value you pass in defines the return type. You can create this as:
System.Func<AnyParameters, ReturnType> myFunc.
Example: System.Func<int, string> bob = (x) => { return x.ToString(); }

System.Predicate<T>

A predicate has a return type of a boolean, and you must pass in a parameter value for it. It’s created as:
System.Predicate<Parameter> myPredicate
Example: System.Predicate<int> bob = (x) => { return x > 0; }

Extra Practice

A great way to practice some of these concepts would be to directly use them; make a small game with an event system! Just something small, maybe have some projectiles shooting from the screen, and a player, and you’re able to pause and resume the game with P, and so it will use this EventSystem style to stop the objects from moving while it’s paused.

Support

Are you having trouble with understanding this tutorial? Please feel free to contact me via email at KoseckCory@gmail.com or message me on discord at 7ark#8194.

I would love to get feedback from people so I can add and improve these tutorials overtime. Letting me know what you’re confused about will let me update the tutorials to be more concise and helpful.

If you’re interested in supporting me personally, you can follow me on Twitter or Instagram. If you want to support me financially, I have a Patreon and a Ko-fi.

2 comments on “Programming in C#, Part 7: Delegates & LambdasAdd yours →

Leave a Reply

Your email address will not be published. Required fields are marked *