BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News C# Futures: Closure Annotations

C# Futures: Closure Annotations

Leia em Português

Though on the “Some Interest” list, the next proposal is very controversial. The basic premise for the Lambda Capture Lists proposal is that it would allow more control over how variables are captured in closures.

The proposal begins with capture lists, a concept seen in C++. Here is an example of a normal closure and one with a capture list:

var x = 100;
Func<int> a = () => x * 2;
Func<int> b = [x] () => x * 2;

If this syntax is used, any variable not in the capture list (denoted by brackets [x]) cannot be used inside the anonymous function without triggering a compiler error. If you use an empty list, [], then it will guarantee a closure won’t be created. This can help performance, as non-closure anonymous functions don’t require a memory allocation.

To access the current object from the closure, you would use [this]. This would reduce the occurrences of accidentally capturing the current object, which can lead to memory leaks.

Capturing by Value

There are times when you only need a copy of value inside the closure and don’t want to actually share a variable between the two. Under this proposal, you can indicate that using the capture list.

Func<int> c = [int xCopy = x]() => xCopy * 2;

This is very verbose, so alternatives are being suggested that would mean the same thing.

Func<int> d = [value x]() => x * 2; //this x is a copy
Func<int> e = [val x]() => x * 2; //this x is a copy
Func<int> f = [let x]() => x * 2; //this x is a copy
Func<int> g = [=x]() => x * 2; //this x is a copy

In order to make the normal by reference closure more explicit, this syntax was proposed:

Func<int> h = [ref x]() => x * 2; //x is an alias
Func<int> i = [&x]() => x * 2; //x is an alias

Related to this is the “skinny arrow” proposal. This would implicitly capture all variables by value.

Func<int> j = () -> x * 2; //this x is a copy

Weak Captures

As mentioned before, closures that outlast the function that creates them are a common source of memory leaks. So Miguel do Icaza suggested that weak references be included in the proposal. Stephen Toub offered this syntax for it:

Action k = [weak myObject] () => […]
Action l = [weak this] () => […]
Action m = [wro = new WeakReference(myObject)] () => […]

Criticism

As mentioned in the introduction, this proposal is controversial. No matter which variants you choose, this new syntax add a lot of visual clutter to the code. And for shorter closures, it doesn’t add much information that you wouldn’t already have.

Adding the capture list has to be optional for backwards compatibility. Given how tedious it is, most developers will probably just not bother to type it, which would bring into doubt the usefulness of the feature at all.

Rate this Article

Adoption
Style

BT