I have a confession to make. I kind of dislike Unity developer who has never used C# for anything else. Unity instructs you to use C# in a narrow way and there are tons of C# things they will never learn. C# has the full support of the .NET framework (technically the open source port Mono) Unity developers don't learn about. So here I am, going over a few C# tricks I think all develoeprs should know.


Higher order functions and delegates

This might sound like a very weird concept but in reality its a nice name for methods taking other methods as parameters. In older C# code you would have to look for the delegate implementation but modern C# has two classes to help you with higher order function: Action and Func. Action is used when a method don't return anything (void) and func is used whenever the method is expected to have a return value.

So the question now is how we can use them.


Simple Func example

Lets assume we have a list of Users we want to filter based on the IsActive flag.

public class User {
  public int Id;
  public string Username;
  public bool IsActive;
}

The most straight forward solution would be to iterate over the list and add the valid ones to a new list with a foreach loop.

List<User> users = db.GetUsers();

var activeUsers = new List<User>();
foreach(var user in users){
	if(user.IsActive){
      activeUsers.Add(user);
}

This is the most common implementation for this I've seen. They're everywhere. The issue is that we're using a lot of code to do vert little. The only part we care about is the

if(user.IsActive)
check. The rest is the same for every filter implementation.


Lets instead create a new higher order filter function that we can call when we want to filter a list. We will implement it as an extension method (more about them later).


public static class CollectionExtensions {
   public static IList<T> Filter<T>(this IEnumerable<T> collection, Func<T, bool> predicate){
      var result = new List<T>();
      foreach(var value in collection){
         // This invokes the method delegate
         if(predicate(value)){

            result.Add(value);
         }
      }
      return result;
   }
}

Now I can easily here you argue that this method looks awfully much like the previous implementation so what have we actually gained by this?


The answer is quite simple: We abstracted away the boilerplate for iterating over a list and invoke some kind of predicate. Whenever you need to filter a list from now on you can call this method. The only thing to supply for the filter method is the actual criteria we are filtering on. The previous code snipped can now be written as this.

List<User> users = db.GetUsers();
var activeUsers = users.Filter(u => u.IsActive);

Imagine we want to filter on something else? Easily done!

List<User> users = db.GetUsers();
var usersWithIdMoreThen100 = users.Filter(u => u.Id > 1000);

Or if we want to filter on multiple fields we can keep chaining them.

public class User {
  public int Id;
  public string Username;
  public bool IsActive;
  public int Score;
  public DateTime PaymentExpirationDate;
}

List<User> users = db.GetUsers();
var activeUsersWithHighScore = users
    .Filter(u => u.IsActive);
    .Filter(u => u.PaymentExpirationDate < DateTime.Now)
    .Filter(u => u.Score > 1000)

This is a very useful method we've built that can be reused and repurposed. The code gets less cluttered and more readable.

In reality, there is no reason to built this extension method because there is already one available in .NET called .Where() in the

System.Linq
namespace.


A simple Action example

Imagine you are creating a menu and have a couple of buttons you're adding programmatically. You want them to look and behave the same, but should behave differently when clicked. An object-oriented approach would be to use inheritance. Create a base class for buttons with a virtual method for when pressing on it.


public class Button {
    public string Name;
    // ... lots of shared variables and behaviour
    public virtual void OnPress() {}
}

public class BackButton: Button {
    public override void OnPress(){
        // ... do something to go back
}

public class JoinButton: Button {
    public override void OnPress() {
        // ... do something to join
    }
}

public class ExitButton: Button {
    public override void OnPress() {
        // ... do something to exit
    }
}

public class Menu {
    public List<Button> Buttons;
   
    public Menu(){
        Buttons = new List<Button>{
            new BackButton { Name = "Back" },
            new JoinButton { Name = "Join" },
            new ExitButton { Name = "Exit" }
        }
    }
    
    public void PressButtonIndex(int index){
        Buttons[i].OnPress();
    }
}

There is once again lots of repetition, boilerplate code, to create the classes when all we want is to override a single method. If you want to, you can think of Funcs and Actions as single method interfaces.

public Func<T, bool>
means whatever we put here has to be a method taking a T value as its first parameter and returns a bool, just like an interface declaration. The difference is we don't specify a name for it.


Lets remake the menu but instead of inheritance, lets use store an Action delegate.

public class Button {
   public string Name;
   // ... lots of shared variables and behaviour
   public Action OnPress;
} 

public class Menu {
   public List<Button> Buttons;

   public Menu(){
      Buttons = new List<Button>{
         new Button { Name ="Back", OnPress = () => // .. something to go back },
         new Button { Name ="Join", OnPress = () => // .. something to join },
         new Button { Name ="Exit", OnPress = () => // .. something to exit },
      }
   }

   public void PressButtonIndex(int index){
      Buttons[i].OnPress();
   }
}

There is no longer a need to create a class for every button type to override behavior as that can be done on the fly.


Another simple solution to show an Action used in a scenario outside of game development is Web requests. A response usually comes back withing a few hundred milliseconds but we don't want the code to block the thread while we wait for a response but at the same time we would want the code handling the response at the same place where we did the request.

public class WebService{
    public string Url;
    public WebService(string url){
        Url = url;
    }
    
    public Get(string path, Action<Response>){
        // .. do an actual request
    }
    
    public Post(string path, object body, Action<Response>){
        // .. do an actual request
    }
}

var webService = new WebService("http://somewebservice.com");

webService.get("/api/users/10", response => {
   // ... callback where we can handle the response when we get it
   if(response.statusCode == 200){
      // ... handle success
   }else {
      // ... handle error
   }
});

var blogPost = new BlogPost();
webService.post("/api/blogpost/", blogPost, response => {
   // .. handles a response on the post request
});

We don't know beforehand in which order these two callbacks will run, because we don't know in which order the server will respond the the queries. But we can still make the call and write the logic to handle the response in the same place.


Considerations

Just like with everything, higher order functions is not a silver bullet. Even if they make your code shorter and more readable, it can come at the expense of performance. In the filter example, in the first implementation it was obvious a new list was allocated and later returned. It the second implementation that is still true, but the allocation of the new list was hidden behind the abstraction and not obvious. It you're not careful you can end up losing performance to unforeseen allocations and redundant iterations.


Another risk I haven't even mentioned so far are so-called clojures. If your predicate delegate in any way uses variables from the outside of its own scope, that is still allowed but the C# runtime will handle that by capturing the method and its surrounding scope is a clojure, which is a object and requires an allocation.

List<User> users = db.GetUsers();

// Pure lamba expression. The predicate is not reliant on anything external
var firstUser = users.Filter(u => u.Id == 0);

// Requires a clojure, as desiredUserId is outside of the scope of the predicate method
var desiredUserId = 0;
var firstUser = users.Filter(u => u.Id == desiredUserId);


Next up, I will go over how higher order functions are used in some .NET libraries for you to use.