Create better code using delegates and extension methods in Unity

Adam Ramberg
5 min readFeb 16, 2019

--

Delegates and extension methods are tools in your C# toolbox that, if used right, can greatly enhance the quality of your code, improve its reusability and readability. But what are delegates and extensions? Here is an explanation of what delegates are from one of the first hits on Google:

A delegate in C# is similar to a function pointer in C or C++. Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code which can call the referenced method, without having to know at compile time which method will be invoked. — akadia.com

In other words, a delegate declaration describes a method signature (a method’s return type and its argument types), while an instance of that delegate can be assigned, referenced and invoke a method matching that signature. You have probably encountered Action, Func and EventHandler before, they are all examples of delegates. If you have ever used functions like Array.Find or List.ForEach, then you have also used delegates. Here is an example of a delegate that takes an int as a parameter and returns a bool:

Extension methods are explained like this in the official .NET C# documentation:

Extension methods enable you to “add” methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type.

The most commonly used extension methods are the ones from LINQ. However, there is nothing stopping you from adding your own extension methods. Below follows an example of adding a chainable add method to the generic List class:

🦄 The good

Great, so now you know what delegates and extensions are! But why should you use them in your code? Here is a quick list of advantages of using delegates and extensions:

  • Encapsulation and code reusability
  • Extensibility of your functions and built in C# types and classes
  • Code that is easier to understand, maintain and read

Let’s show this in an example. I usually find myself in my games iterating over a collection of items and perform an action or change its state depending on a condition. Imagine that you have an Unicorn that likes to give hugs:

And let’s say that you also have an unicorn controller class (shame on you) that iterates over the unicorns and makes them hug your player if it comes too close:

It works and all, but an approach like this soon becomes really messy, especially when we need to expand on the logic. An alternative to this would be to create an extension method that is using delegates for iterating a List:

You can now rewrite your previous unicorn controller class to this:

That is some neat looking code! 👌

From my experience these kind of patterns are used over and over again and writing those extension methods are worth while your time. The extension methods could also be reused, not only in this project, but in your other projects as well (maybe by creating your own Unity package), which will save you a lot of time in the long run. Having the implementation for the ForEach method only defined once makes your code base also more maintainable. Furthermore, I find the second approach much easier to read and grasp at first glance.

👾 The bad

That is all great! However, there are also some potential pitfalls using delegates and extensions like this in Unity:

  • Learning curve
  • Unintentional creation of garbage (GC)

Many people finds it hard to wrap their head around delegates and why they are sometimes preferred over regular function calls. There is an initial learning curve, but once you get passed it they are easy to grasp. If you are creating a library of extension methods using delegates it can also become hard for new developer to understand the code right away (good names for your extension methods are really key here). You can always point to this blog post in order to get rid off the learning curve problem 😊 However, the unintentional creation of garbage is worse of a problem and could potentially create a world of hurt…

💩 The ugly

I came across this blog post by Jackson Dunstan the other day. It explains really well how easy it is to unintentionally create garbage while using delegates when coding C# in Unity. Head over to Jackson’s blog post and read it while I will wait here for you.

When reading the blog post I was surprised by two things. First, that passing an instance method to a method that takes a delegate actually creates garbage every time you call that method. Second, I was expecting that a lambda function passed as a delegate would create garbage every time it was used, but the C# compiler actually caches the lambda function for you. However, if the lambda function (or an anonymous method) is using variables or data from the outside scope, it won’t be cached and will instead create garbage each call, just like the use of instance methods. Here follows some tips for what to avoid when using delegates in Unity:

  • Don’t pass instance methods directly to methods taking a delegate as a parameter. There are two alternatives 1) use lambdas, static or anonymous methods or 2) you can cache the instance method in a variable and pass that variable.
  • When relying on the C# compiler to cache your delegate (when passing lambdas or anonymous functions directly), then never reach for variables that are outside the method’s own scope. Think of those methods as static (or pure / functional).

Jackson is further talking about the expense of branching. There is a difference between if you pass a method directly to a method taking a delegate versus if you cache your delegate in a variable and pass that variable instead. In the former there will be a check against the C# compiler’s own cache and we will always do a cache check (that according to Jackson can hurt performance) while in the latter the compiler will still keep its cache field meaning that there are duplicate cache fields (yours and the compiler’s). I would argue that the performance difference between these are in general too small to care about. When I was benchmarking this on my computer I could not see a big difference in execution time. So I will personally in most cases just not cache my delegates until it actually becomes a real problem.

🤔 Conclusion

There are some great benefits of using delegates and extension methods in this way, which makes your code reusable, readable and more concise. Just keep in mind how to avoid creating unintentional garbage. Let me know what you think in the comments below and hit that 👏 button if you found what you just read interesting. You can also hit me up on Twitter if you got any questions or comments on what you just read.

--

--