Ralph Romero

Blog

Applying C# Closures

Posted at — Jun 15, 2020

There are many articles that describe what C# closures are, but it can be difficult to understand where or why you should use them. I’ll walk you through an example which will give you a better appreciation of the where and why. I assume you have already read other articles which describe what Closures are and how to implement them in C#.

Problem Statement

We require a function that will give the word for hello in random different languages. It should ensure that the same word isn’t repeated until all the different versions of the word have been used at least once.

The Object Oriented Approach

Recognise that we need a function that maintains an internal state so a class would be suitable.

public class HelloMaker
{
    List<String> helloList = new List<String>() { "Konnichiwa", "Prvyet", "Xin Chao", "Ni Hao" };
    List<String> usedHelloList = new List<string>();

    public string GetHello()
    {
        if (usedHelloList.Count == helloList.Count)
            usedHelloList = new List<string>();
        var unusedHelloList = helloList.Where(x => !usedHelloList.Contains(x));
        var rand = new Random();
        var hello = unusedHelloList.ElementAt(rand.Next(unusedHelloList.Count()-1));
        usedHelloList.Add(hello);
        return hello;
    }
}

Now we can call this function.

var hm = new HelloMaker();
foreach(int i in Enumerable.Range(1,5))
{
    Console.WriteLine(hm.GetHello());
}

and we get

Prvyet
Xin Chao
Konnichiwa
Ni Hao
Konnichiwa

The key point here is that the internal state of what hello has been used is wrapped up with the method as a class.

The Closure Approach

Now let’s write a Closure that will give us the same results.

Func<string> GetHelloFunc()
{
    var helloList = new List<String>() { "Konnichiwa", "Prvyet", "Xin Chao", "Ni Hao" };
    var usedHelloList = new List<string>();
    Func<string> h = delegate ()
    {
        if (usedHelloList.Count == helloList.Count)
            usedHelloList = new List<string>();
        var unusedHelloList = helloList.Where(x => !usedHelloList.Contains(x));
        var rand = new Random();
        var hello = unusedHelloList.ElementAt(rand.Next(unusedHelloList.Count() - 1));
        usedHelloList.Add(hello);
        return hello;
    };
    return h;
}

Then we can call the function in a similar way to get the same results.

var closure = GetHelloFunc();
foreach (int i in Enumerable.Range(1, 5))
{
    Console.WriteLine(closure());
}

What about a Function Approach?

Implementing this as a plain function would require us to have the list variables declared in the calling function. We would then risk unintended mutation from other sources. We also wouldn’t be able to pass this function around and have the function called from other places.

Summary

In the OO approach, we created a class just for the purpose of creating a single function. What we really wanted was just the one function. By using Closures, we get concise expression through not defining a class.

If you don’t care about Closures you will be fine using OO, but Closures can be used when: