DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

.NET Gotcha – Loop Variables and Closures

Slobodan Pavkov user avatar by
Slobodan Pavkov
·
Oct. 06, 12 · Interview
Like (0)
Save
Tweet
Share
4.00K Views

Join the DZone community and get the full member experience.

Join For Free

try to guess the output of this simple console application:

class program
{
    static void main()
    {
        var actions = new list();

        for (var i = 0 ; i < 10; i++)             
        {                 
            var writetoconsoleaction = new action(() =>
                {
                    console.writeline(i);
                });
            actions.add(writetoconsoleaction);
        }

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

        console.readline();
    }
}

one would expect to see numbers from 0 to 9. but here is the actual output of the app:



okay, that’s strange, right?

turns out it's like that by design. what you have there is a closure over the loop variable. and closures in c# are done around variables and not around specific values of those variables – so that means that lambda expression gets to use the actual reference to the closed variable.

let me explain in a little bit more detail:

in the first loop we iterate increasing the counter variable from 0 to 9 and then use it in the lambda expression. that means each lambda gets its access to the counter via closure.

so at the end of this first loop, value of the counter is 10 (it's 10 because we used a post-increment operator: counter++ , so in the last iteration of the loop we increase counter to 10 and then check if it's <10. we see it's not, so we exit the loop).

afterward, we start executing those actions we created in first loop, and they all have a closure over this same variable counter – which has a value of 10 and therefore each action prints out its current value -- 10 -- to the console.

so it works as expected, once you know what to expect.

and this is a really common mistake among c# developers.

how do you fix it?

the solution is very simple, once you know what's going on. all you need to do is this: instead of passing the variable counter to the lambda expression, create a local copy of this variable and pass this copy to the lambda instead of passing the counter .

now each time, closure will occur around that copy variable that has the current value of the counter at that moment of execution, and this local copy value will stay that way and will not be changed afterwards by the loop.

later, when the actions are executed (in the second loop), each will use their own closure around copies of the counter and therefore each will have its own different, expected value.

here is the code that works as we expect:

class program
{
    static void main()
    {
        var actions = new list();

        for (var i = 0 ; i < 10; i++)             
        {                 
                int countercopy = i;                 
                var writetoconsoleaction = new action(() =>
                {
                    console.writeline(countercopy);
                });
            actions.add(writetoconsoleaction);
        }

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

        console.readline();
    }
}

as you see, we create a copy of the counter called countercopy and pass this to the lambda expression each time.

this may be old news to some, but there are many c# developers out there who are unaware of this behavior (or tend to forget it from time to time), so make sure to spread the word and always remember it.

by the way, the c# team is changing this in c# version 5 to work as one would expect but until then we just need to copy those loop variables manually.

IT Console (video game CLI) Pass (software) dev app Moment application Execution (computing)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Pair Testing in Software Development
  • Tech Layoffs [Comic]
  • Core Machine Learning Metrics
  • Uplevel Your Managers With Mini-M Support Groups

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: