Let me apologize in advance - I'm probably butchering the terminology. I have a vague understanding of what a closure is, but can't explain the behaviour I'm seeing. At least, I think it's a closure issue. I've searched online, but haven't found the right keywords to get what I want.
Specifically - I have two blocks of code that are REALLY SIMILAR (at least to my eyes). First:
static void Main(string[] args) { Action x1 = GetWorker(0); Action x2 = GetWorker(1); } static Action GetWorker(int k) { int count = 0; // Each Action delegate has it's own 'captured' count variable return k == 0 ? (Action)(() => Console.WriteLine("Working 1 - {0}",count++)) : (Action)(() => Console.WriteLine("Working 2 - {0}",count++)); }
If you run this code and invoke x1() and x2() you'll see that they maintain a separate 'count' value.
foreach(var i in Enumerable.Range(0,4)) { x1(); x2(); }
Outputs:
Working 1 - 0 Working 2 - 0 Working 1 - 1 Working 2 - 1 Working 1 - 2 Working 2 - 2 Working 1 - 3 Working 2 - 3
That makes sense to me and matches the explanations I've read. Behind the scenes a class is created for each delegate/action and the class is given a field to hold the value of 'count'. I went to bed feeling smart!
BUT THEN - I tried this very similar code:
// x3 and x4 *share* the same 'captured' count variable Action x3 = () => Console.WriteLine("Working 3 - {0}", count++); Action x4 = () => Console.WriteLine("Working 4 - {0}", count++);
And (like the comment says) the behavior is completely different here. x3() and x4() seem to have the SAME count value!
Working 3 - 0 Working 4 - 1 Working 3 - 2 Working 4 - 3 Working 3 - 4 Working 4 - 5 Working 3 - 6 Working 4 - 7
I can see what's happening - but I don't really get why they are treated differently. In my head - I liked that original behaviour I was seeing, but the later example confuses me. I hope that makes sense. Thanks
A lace frontal or a lace closure? The truth is, both are great. Both will give you a full and flawless install with the illusion that hair is growing directly from your scalp. Closures and Frontals allow you to braid and protect your natural hair.
Lace closures last for longer Whether you're going for a sew-in or lace closure wig, they typically last longer than a frontal, with proper maintenance. Lace closures are more suited for warmer weather.
Beneficial: Lace closure doesn't cause any harm to your hairline and scalp. Because you can put it off easily at the end of day so that your hair can breathe. Also lace closure protects your hair from heat styling, backcombing, etc.
Your first example had two different int count
variable declarations (from the separate method calls). Your second example is sharing the same variable declaration.
Your first example would behave the same as the second example had int count
been a field of your main program:
static int count = 0; static Action GetWorker(int k) { return k == 0 ? (Action)(() => Console.WriteLine("Working 1 - {0}",count++)) : (Action)(() => Console.WriteLine("Working 2 - {0}",count++)); }
This outputs:
Working 1 - 0 Working 2 - 1 Working 1 - 2 Working 2 - 3 Working 1 - 4 Working 2 - 5 Working 1 - 6 Working 2 - 7
You can simplify it without the ternary operator as well:
static Action GetWorker(int k) { int count = 0; return (Action)(() => Console.WriteLine("Working {0} - {1}",k,count++)); }
Which outputs:
Working 1 - 0 Working 2 - 0 Working 1 - 1 Working 2 - 1 Working 1 - 2 Working 2 - 2 Working 1 - 3 Working 2 - 3
The main issue is that a local variable declared in a method (in your case int count = 0;
) is unique for that invocation of the method, then when the lambda delegate is created, each one is applying closure around its own unique count
variable:
Action x1 = GetWorker(0); //gets a count Action x2 = GetWorker(1); //gets a new, different count
A closure captures a variable.
A local variable is created when a method is activated by being called. (There are other things that create local variables but let's ignore that for now.)
In your first example you have two activations of GetWorker
and therefore two completely independent variables named count
are created. Each is captured independently.
In your second example, which unfortunately you do not show all of, you have a single activation and two closures. The closures share the variable.
Here's a way to think about it that might help:
class Counter { public int count; } ... Counter Example1() { return new Counter(); } ... Counter c1 = Example1(); Counter c2 = Example1(); c1.count += 1; c2.count += 2; // c1.count and c2.count are different.
Vs
void Example2() { Counter c = new Counter(); Counter x3 = c; Counter x4 = c; x3.count += 1; x4.count += 2; // x3.count and x4.count are the same. }
Does it make sense to you why in the first example there are two variables called count
that are not shared by multiple objects, and in the second there is only one, shared by multiple objects?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With