Are Lambda Expressions in C# Closures

Are Lambda expressions in C# closures?

A lambda may be implemented using a closure, but it is not itself necessarily a closure.

A closure is "a function together with a referencing environment for the non-local variables of that function.".

When you make a lambda expression that uses variables defined outside of the method, then the lambda must be implemented using a closure. For example:

int i = 42;

Action lambda = () => { Console.WriteLine(i); };

In this case, the compiler generated method must have access to the variable (i) defined in a completely different scope. In order for this to work, the method it generates is a "function together with the referencing environment" - basically, it's creating a "closure" to retrieve access to the variable.

However, this lambda:

Action lambda2 = () => { Console.WriteLine("Foo"); }

does not rely on any "referencing environment", since it's a fully contained method. In this case, the compiler generates a normal static method, and there is no closure involved at all.

In both cases, the lambda is creating a delegate ("function object"), but it's only creating a closure in the first case, as the lambda doesn't necessarily need to "capture" the referencing environment in all cases.

How closure in c# works when using lambda expressions?

The key point here is that closures close over variables, not over values. As such, the value of a given variable at the time you close over it is irrelevant. What matters is the value of that variable at the time the anonymous method is invoked.

How this happens is easy enough to see when you see what the compiler transforms the closure into. It'll create something morally similar to this:

public class ClosureClass1
{
public int i;

public void AnonyousMethod1()
{
Console.WriteLine(i);
}
}

static void Main(string[] args)
{
ClosureClass1 closure1 = new ClosureClass1();
for (closure1.i = 0; closure1.i < 10; closure1.i++)
new Thread(closure1.AnonyousMethod1).Start();
}

So here we can see a bit more clearly what's going on. There is one copy of the variable, and that variable has now been promoted to a field of a new class, instead of being a local variable. Anywhere that would have modified the local variable now modifies the field of this instance. We can now see why your code prints what it does. After starting the new thread, but before it can actually execute, the for loop in the main thread is going back and incrementing the variable in the closure. The variable that hasn't yet been read by the closure.

To produce the desired result what you need to do is make sure that, instead of having every iteration of the loop closing over a single variable, they need to each have a variable that they close over:

for (int i = 0; i < 10; i++)
{
int copy = i;
new Thread(() => Console.WriteLine(copy));
}

Now the copy variable is never changed after it is closed over, and our program will print out 0-9 (although in an arbitrary order, because threads can be scheduled however the OS wants).

Closure lambda expression in C#

Ah, good ol' closure over the iterator variable ;p

Move the int iter:

int iter = i;
ThreadPool.QueueUserWorkItem((o) => {
makeServiceCall(iter);
});

Currently you are capturing something that is changing with the loop, and will usually be after the loop by the time the threads kick in. Alternatively, use the arg o:

ThreadPool.QueueUserWorkItem((o) => {
int iter = (int)o;
makeServiceCall(iter);
}, i);

This passes a boxed copy of i at the time the item is queued, so it won't change.

In case the issue isn't clear: variables that are captured into a lambda retain their original declaration point - it is a full lexical closure. The i inside QueueUserWorkItem is not "the value of i at the time this lambda was created", but rather, is "the value of i now". In most cases, the for loop will out-pace thread-creation/pickup by a massive margin, so by the time the threads have started the for loop has finished and the value of i is now out of bounds for the array.

Closures and Lambda in C#

Yes, FindAll will create a new list. You want "Where", which will return an IEnumerable object that knows how to loop over your existing list:

foreach (string name in names.Where(n => n.StartsWith("C") ) ) 
{
Console.WriteLine(name);
}

But there's no closure in that code, because there's no local variable to capture.

Issue with closure variable capture in c# expression


I don't seem to be able to find any hoisted local variables within the method so I'm wondering whether they're being captured at all?

It looks like you are building the expression tree lambda yourself, by "manually" calling the factory methods. The compiler has no idea that that's what you're doing; it just sees method calls. If you want locals to be hoisted then you're going to have to either (1) get the compiler to do it for you, by making it rewrite the lambda, or (2) hoist 'em yourself.

That is:

int x = 123;
Expression<Func<int>> ex = ()=>x;

the compiler rewrites the lambda and hoists it for you, as though you'd said:

Closure c = new Closure();
c.x = 123;
Expression<Func<int>> ex = ()=>c.x;

Where c typically becomes a Constant expression.

But if you say

Expression<Func<int>> ex = Expression.Lambda( ...something that uses x ... );

the compiler has no idea that you're doing something where it needs to hoist x; x is not inside a lambda expression. If you're using the factories, the compiler assumes you know what you're doing and doesn't mess around with rewriting it. You'll have to hoist it yourself.

What is the impact of C# closures on memory?

The closure is going to store the value of the test variable, and the test variable is just a reference to an object of type Test elsewhere in memory, since it's not a struct, and that Test object doesn't actually have an integer array, it just has a reference to a large array stored in yet another location in memory.

Since you're holding onto a reference to that instance of Test, the object won't be eligible for garbage collection for as long as the closure isn't eligible for garbage collection. If you pull the boolean value out of the Test object and close over that, as you showed, then you're no longer referencing theTest object. If, as a result of that, nothing has access to the Test instance, or the contained array, then it would become eligible for garbage collection. If there would still be other code that could access it, then that wouldn't be the case, and there'd be no benefit.

What is the difference between a 'closure' and a 'lambda'?

A lambda is just an anonymous function - a function defined with no name. In some languages, such as Scheme, they are equivalent to named functions. In fact, the function definition is re-written as binding a lambda to a variable internally. In other languages, like Python, there are some (rather needless) distinctions between them, but they behave the same way otherwise.

A closure is any function which closes over the environment in which it was defined. This means that it can access variables not in its parameter list. Examples:

def func(): return h
def anotherfunc(h):
return func()

This will cause an error, because func does not close over the environment in anotherfunc - h is undefined. func only closes over the global environment. This will work:

def anotherfunc(h):
def func(): return h
return func()

Because here, func is defined in anotherfunc, and in python 2.3 and greater (or some number like this) when they almost got closures correct (mutation still doesn't work), this means that it closes over anotherfunc's environment and can access variables inside of it. In Python 3.1+, mutation works too when using the nonlocal keyword.

Another important point - func will continue to close over anotherfunc's environment even when it's no longer being evaluated in anotherfunc. This code will also work:

def anotherfunc(h):
def func(): return h
return func

print anotherfunc(10)()

This will print 10.

This, as you notice, has nothing to do with lambdas - they are two different (although related) concepts.

Does the C# compiler treat a lambda expression as a public or private method?

It depends. With the current version of Visual Studio, the methods that implement lambdas are never public, but they're not always private. A simple program to test some versions of lambdas:

public class Program
{
public static void Main()
{
var program = new Program();
Try("A", program.A);
Try("B", program.B);
Try("C", program.C);
Console.ReadKey();
}

private static void Try(string name, Func<Action> generator)
{
var mi = generator().Method;
Console.WriteLine($"{name}: DeclaringType={mi.DeclaringType}, Attributes={mi.Attributes}");
}

private Action A() => () => { };
private Action B() => () => { ToString(); };
private Action C()
{
var c = 1;
return () => c.ToString();
}
}

prints

A: DeclaringType=Scratch.Program+<>c, Attributes=PrivateScope, Assembly, HideBySig
B: DeclaringType=Scratch.Program, Attributes=PrivateScope, Private, HideBySig
C: DeclaringType=Scratch.Program+<>c__DisplayClass4_0, Attributes=PrivateScope, Assembly, HideBySig

A's lambda doesn't have any captures. It's created as an internal method of an empty closure class.

B's lambda captures this. It's created as a private method of the containing class.

C's lambda captures c. It's created as an internal method of a non-empty closure class.

All of this is undocumented and has changed in the past, so it would be good to avoid relying on it. What matters is that when you call the anonymous method, it behaves as specified. If you need anything more than that, you shouldn't be using anonymous methods. Depending on what you're after, you might either still be able to use lambdas, but with expression trees, or you might need to create regular named methods instead.



Related Topics



Leave a reply



Submit