0

I have this rather simple logic:

class Program
{
    static void Main(string[] args)
    {
        using (TransactionScope ts = new TransactionScope())
        {
            System.Threading.Tasks.Parallel.Invoke(() =>
                {
                    TransactionScope y = ts;
                    System.Diagnostics.Debug.WriteLine("Test");
                },
                () =>
                {
                    System.Diagnostics.Debug.WriteLine("Test");
                }
            );
            ts.Complete();
        }        
    }
}

If you place breakpoints on the two Debug.WriteLine() statements, you'll notice that when it breaks on the first, both y and ts are listed as locals by the debugger. But when it hits the breakpoint in the latter, ts is not listed as a local and furthermore, adding ts to the watch window gives The name 'ts' does not exist in the current context.

Is this variable capturing in action or is this some other mechanism? I've looked around at writeups on variable capture and I can't find anything that explicitly states that variables are only captured when they're used, but I'm making the assumption that it's called variable capture because it only "captures" what it needs and doesn't keep references to everything available.

Pete
  • 6,585
  • 5
  • 43
  • 69
  • I think the code as written might be in danger of calling `Complete` before the possibly parallel actions are complete. – Kit Sep 24 '13 at 15:44
  • @Kit, From the docs for Parallel.Invoke: `This method does not return until each of the provided operations has completed, regardless of whether completion occurs due to normal or exceptional termination.` – Pete Sep 24 '13 at 17:17
  • Oh duh. I forgot this. Thanks for the reminder. – Kit Sep 24 '13 at 17:37

1 Answers1

3

I'm making the assumption that it's called variable capture because it only "captures" what it needs and doesn't keep references to everything available

That's exactly right. The compiler refactors code that closes over a variable to keep it in scope. It doesn't close over every single outer variable when an anonymous method is used.

Take the following example:

public static void Foo()
{
    int i = 0;
    int j = 1;
    Action a = () => Console.WriteLine(i);
}

It will be turned into something like the following by the compiler:

public class ClosureClass1
{
    public int i;

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

public static void Foo()
{
    ClosureClass1 closure = new ClosureClass1();
    closure.i = 0;
    int j = 1;
    Action a = closure.Method1;
}

You should be able to see, from this example, why the closed over fields are accessible and the fields not closed over from an outer scope are not.

Dave Zych
  • 21,581
  • 7
  • 51
  • 66
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Adding lambda with no captured variable also as an example would be even better. `[CompilerGenerated]private void CompilerGeneratedName() { Debug.WriteLine("Test"); }` In the same class of Enclosing Type – Sriram Sakthivel Sep 24 '13 at 14:43