Delegates, events, lambdas and all things callbacky! (Part 2)

Part two of my delegatey thing begins with lambdas, as promised.

Lambdas are basically a shorter form of anonymous functions than the delegate keyword version.

A few quick examples:

x=>Debug.Log(x);
()=>Debug.Log("Something happened!");
(x, y) => x * y;
x=>{ Debug.Log(x); return x*2; }

The first one takes one parameter and calls Debug.Log on it. You would use this with Action.

The second takes no parameters and calls Debug.Log. For this, simply Action.

The third one takes two parameters and returns the result of the multiplication of the two. This one is Func, e.g. Func.

The last one takes in a single parameter and does two things to it, first printing it with Debug.Log, then returning the value doubled. When using parentheses with lambdas, if the delegate expects a returned value, the return is no longer implicit.

Now, a quick example using lambdas to do something. This time, I’ll show how to inject logic into a function using them:

void Start()
{
	int[] array = new int[]{0,1,2,3,4,5,6,7,8,9};
	DoStuffToAll(array, i => Debug.Log(i.ToString()));
}

void DoStuffToAll(int[] array, Action<int> action)
{
	foreach(int i in array)
	{
		action(i);
	}
}

This code basically loops through all items in the array and performs the action on each, in this case Debug.Log, printing out the values from 0 to 9. As usual, this snippet isn’t terribly useful, as it’s basically the same as a foreach in Start, but the ideas will translate to much more difficult problems.

To expand on it, you could make it do multiple things in one go. What I haven’t yet mentioned about lambdas is that you can encapsulate local variables in them, which means you can do some fairly cool things. This next example does just that, using the same DoStuffToAll from above:

void Start()
{
	int[] array = new int[]{0,1,2,3,4,5,6,7,8,9};
	int total = 0;
	DoStuffToAll(array, i => { Debug.Log(i.ToString()); total += i; });
	Debug.Log("Total is: " + total);
}

This encapsulates an alias (Think along the lines of a reference) to the total variable and increments it by each value in the array. This also works for variables in classes, or anything else you want, but be careful with it, it’s fairly easy to trip up and do something you’re not expecting!

So, now that we’re fairly familiar with lambdas and injecting functionality into other functions, let’s look at events.

Events are similar to properties, but work with delegates. They stop the delegate from being modified or called from outside the instance of the class it belongs to. This ties back into my post about sane data encapsulation. You don’t want other classes to be able to do otherClass.yourDelegate = null, as that could potentially have breaking changes, but you do want to have the option of adding or removing from the delegate.

To show this in action, here is a small example:

void Start()
{
	MyEventTest test = new MyEventTest();
	test.Completed += ()=>Debug.Log("Completed!");
	test.DoSomething();
}

public class MyEventTest
{
	public event Action Completed;

	public void DoSomething()
	{
		for(int i = 0; i < 10; ++i)
		{
			Debug.Log(i.ToString());
		}
		if (Completed != null)
			Completed();
	}
}

The event is defined using Action as the delegate type, with the variable name Completed. After creating an instance of the class, the event is assigned to using the += operator, which is how you add your own delegate to the event, and then the DoSomething method is called. This method does its stuff then calls Completed, assuming it’s been assigned to, which in turn invokes the lambda we assigned to it.

There is a way to omit the null check by assigning an empty delegate list to the event, but that forces creation of the delegate, which causes extra memory overheads, so be aware of that.

To do it, you simply use this:

public event Action Completed = delegate{};

When to use events and when to use delegates is a fairly interesting design choice, but basically boils down to this: if it needs to be a public class member, use an event, otherwise, use a delegate. Events won’t work as parameters to functions as they’re a set of functions themselves, so bear that in mind.

Put all of the above together and you can create some fairly amazing things.

And with that, part two is concluded!

This entry was posted in C#, Unity. Bookmark the permalink.

One Response to Delegates, events, lambdas and all things callbacky! (Part 2)

  1. Pingback: Fun with Generics and Lists in C# - technical architecture.

Comments are closed.