C# vs C++ - Implicit closures can be a pain sometimes

Yesterday I’ve been confronted to an odd problem while working with C#, a language I have a deep respect for and that I consider to be one of the best object oriented language, with all the flaws that this qualification entails. One of his major drawbacks is that, like in Java, you don’t have the hands on the memory. Everything relating to allocation and destruction is handled by a garbage collector.

Now, like many other programming languages, the designers of C# decided to include several features that allow to make use of certain aspects of functional programming in your projects. The issue I’ve been faced with stems directly from this desire to have a functional approach in a managed language.

Context

The context is pretty simple, while I have simplified the code examples I will show you later on it’s good to have an idea of what I was trying to attempt at the time.

I’m working on a desktop application using Unity3D. An odd choice, I’ll give you that, but it’s justified, trust me on that. While I’m still in the early developement stagei, most of the architecture is done, and I have a pretty clear idea of where I’m going. There’s already a decent amount of code laid out. One of the crucial aspect of many deskop app is the menu bar, that thing that sits on top of your windows and that give you dropdown menus when you click on it. Something that would look like that:

In the original program I would create a Dictionary of Lists of sub menu entries, the key being the top entry on the menu bar. While running the code, I would end up with all sub menus containing exactly the sames entries as the very last sub menu declared. This puzzled me for a while.

Reproducing (C#)

Now here’s an extremely simplified code that reproduces the issue I faced:

using UnityEngine;
using System.Collections.Generic;

public class ClosureTest : MonoBehaviour {

    public delegate void TestAction();

    void Start () {
        Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>() {
                { "Foo" , new List<string>() { "Goo", "Hoo", "Ioo" } },
                { "Bar" , new List<string>() { "Car", "Dar", "Ear" } }
            };

            Dictionary<string, TestAction> acts = new Dictionary<string, TestAction>();

        foreach (var l in dict) {
            // Adding actions with the help of a lambda expression
            acts.Add(l.Key, () => {
                foreach (var s in l.Value) {
                    Debug.Log("Submenu : " + s );
                }
            });
        }

        foreach (var l in acts) {
            Debug.Log("Menu name : " + l.Key);
            l.Value();
        }
    }
}

The example code up there reproduces that issue nicely. You can test it by creating an empty Unity3D project, creating an empty GameObject in the scene, creating a script named ClosureTest, paste the code in it and launch the playback. You could adapt that problem to run on a standard C# project with your Visual Studio or your MonoDevelop, the problem isn’t specific to the platform.

From reading the code you would expect to get the following output:

Menu name : Foo
Submenu : Goo
Submenu : Hoo
Submenu : Ioo
Menu name : Bar
Submenu : Car
Submenu : Dar
Submenu : Ear

However, the output you’ll get is the following:

Menu name : Foo
Submenu : Car
Submenu : Dar
Submenu : Ear
Menu name : Bar
Submenu : Car
Submenu : Dar
Submenu : Ear

It’s almost like the last lambda wasn’t set properly for some weird reason.

Explanation

I was already well on my way to solve the issue when a colleague of mine mentionned that he had suffered from a disturbingly similar issue a while back when working with Python.

What’s happening is in fact pretty simple if you keep in mind that C# is managing the memory for you and you’re mostly handling references.

When the anonymous function is created here in the form of a lambda,

acts.Add(l.Key, () => {
    foreach (var s in l.Value) {
        Debug.Log("Submenu : " + s );
    }
});

it captures a reference to l. The value associated with this reference will change as the foreach instruction goes on. At the end of the foreach loop the reference to l points to it’s last referred value. The closure created always refers to the l, pointing to the last value it had… and as such it access the values pointed during the last iteration… hence the repetition of the last menu entries for earlier keys in our acts Dictionary.

One could say that the values captured by the closure are implicit. No mechanism exist to change that behavior… but we can coerce the compiler into submission.

This issue can be easily fixed by adding an intermediate variable so that the compiler understands that we want to point to a value instead to the ever-changing l reference.

foreach (var l in dict) {
    // Adding actions with the help of a lambda expression
    var val = l; // <----- Creating a new intermediate variable
    acts.Add(val.Key, () => {
        foreach (var s in val.Value) {
            Debug.Log("Submenu : " + s );
        }
    });
}

There we have it. The issue is fixed even if the code looks a bit less concise now.

And what about C++

C++11, a language I’m more familiar with (and even more appreciative than C#, although for different reasons) introduced lamda functions in a way that prevents the issue I faced here. It’s true that it also has to do with the fact that you deal with pointers all the times in C++, but the syntax, while a tad more cumbersome has a strong advantage: it forces the user to explicitly define the captured values of the closure.

When defining a lambda function in C++, one has to declare a “capture list” that tells the compiler how the closure is meant to handle the captured values and references:

[ capture-list ] ( params ) -> ret { body }
[ capture-list ] ( params ) { body }    
[ capture-list ] { body }

While I find myself relying more or less only on the 3 first options (shame on me) I do enjoy the possibility of granularity when it comes to capture selection for closures. This explicit behavior is sane and predictable. It’s a damn shame the designers of C#, in their infinite wisdom, didn’t pick something similar to this.

I guess that not all languages are created equal even when they evolve to mimick each other.



Enter your desired user name/password and after your comment has been reviewed by an admin it will be posted and your account will be enabled. If you are already registered please login before posting.