Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am curious about the actual .NET implementation and the decision behind it.

For example in Java, all captured values used in an anonymous classes are required to be final. This requirement seems to be dropped in .NET.

Also, is there a difference in an implementation of captured values for value types as opposed to reference types?

Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
136 views
Welcome To Ask or Share your Answers For Others

1 Answer

The easiest way of finding out how it's implemented is to try it. Write some code which uses a captured variable, compile it, then look at it in Reflector. Note that it's the variable which is captured, not the value. That's one of the big differences between Java and C# in this area.

The basic idea is that each level of scope containing at least one captured variable results in a new class with fields for the variables which have been captured. If there's more than one level, then an inner scope also has a field for the next scope out, and so on. The genuine local variables on the stack end up being references to instances of the autogenerated classes.

Here's an example:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        for (int i=0; i < 5; i++)
        {
            int copyOfI = i;

            for (int j=0; j < 5; j++)
            {
                int copyOfJ = j;

                actions.Add(delegate
                {
                    Console.WriteLine("{0} {1}", copyOfI, copyOfJ);
                });
            }
        }

        foreach (Action action in actions)
        {
            action();
        }        
    }
}

(You get different results if you don't take a copy of course - experiment!) This is compiled into code like this:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        for (int i=0; i < 5; i++)
        {
            OuterScope outer = new OuterScope();
            outer.copyOfI = i;

            for (int j=0; j < 5; j++)
            {
                InnerScope inner = new InnerScope();
                inner.outer = outer;
                inner.copyOfJ = j;

                actions.Add(inner.Action);
            }
        }

        foreach (Action action in actions)
        {
            action();
        }        
    }

    class OuterScope
    {
        public int copyOfI;
    }

    class InnerScope
    {
        public int copyOfJ;
        public OuterScope outer;

        public void Action()
        {
            Console.WriteLine("{0} {1}", outer.copyOfI, copyOfJ);
        }
    }
}

Every reference to the captured variable ends up going through the instance of the generated class, so it's not just a one-off copy. (Okay, in this case nothing else in the code uses the captured variables, but you can easily imagine it could.) Note how for any one iteration of the outer loop, the five new instances all share one instance of OuterScope. You might want to try playing with extra code in the delegate to see how that affects things - if the delegate changes copyofI that change will be seen in the next delegate; changes to copyOfJ won't be seen because the next delegate will be using a separate instance of InnerScope.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...