Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. The Lounge
  3. Closures (C#)

Closures (C#)

Scheduled Pinned Locked Moved The Lounge
csharpcomperformancehelpquestion
18 Posts 13 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Marc Clifton
    wrote on last edited by
    #1

    I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

    for (int i=0; i <= 10; i++)
    {
    var task = Task.Run(async () =>
    {
    var convertResp = await StartTestConvert(i);
    ... other stuff
    });
    }

    And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

    for (int i=0; i <= 10; i++)
    {
    int n = i;
    var task = Task.Run(async () =>
    {
    var convertResp = await StartTestConvert(n);
    ... other stuff
    });
    }

    I think this would make a good interview question!

    Latest Articles:
    A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

    Richard DeemingR 0 Sander RosselS J J 9 Replies Last reply
    0
    • M Marc Clifton

      I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

      for (int i=0; i <= 10; i++)
      {
      var task = Task.Run(async () =>
      {
      var convertResp = await StartTestConvert(i);
      ... other stuff
      });
      }

      And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

      for (int i=0; i <= 10; i++)
      {
      int n = i;
      var task = Task.Run(async () =>
      {
      var convertResp = await StartTestConvert(n);
      ... other stuff
      });
      }

      I think this would make a good interview question!

      Latest Articles:
      A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

      Richard DeemingR Offline
      Richard DeemingR Offline
      Richard Deeming
      wrote on last edited by
      #2

      Marc Clifton wrote:

      I think this would make a good interview question!

      And I thought this was fairly common knowledge! :laugh: (Not that that would preclude it from being an interview question, of course.) They "fixed" the behaviour for the foreach loop back in C# 5, but deliberately chose not to change the for loop:

      Closing over the loop variable considered harmful, part one | Fabulous adventures in coding[^]:

      UPDATE: We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The for loop will not be changed.


      "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

      "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

      M 1 Reply Last reply
      0
      • M Marc Clifton

        I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

        for (int i=0; i <= 10; i++)
        {
        var task = Task.Run(async () =>
        {
        var convertResp = await StartTestConvert(i);
        ... other stuff
        });
        }

        And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

        for (int i=0; i <= 10; i++)
        {
        int n = i;
        var task = Task.Run(async () =>
        {
        var convertResp = await StartTestConvert(n);
        ... other stuff
        });
        }

        I think this would make a good interview question!

        Latest Articles:
        A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

        0 Offline
        0 Offline
        0x01AA
        wrote on last edited by
        #3

        From my experience there are most probably programmers who would fix it like this ;P :laugh:

        // Make sure the sleep statement takes more time then execution of the for block
        for (int i = 0; i <= 10; System.Threading.Thread.Sleep(10), i++)
        {
        ....
        }

        1 Reply Last reply
        0
        • Richard DeemingR Richard Deeming

          Marc Clifton wrote:

          I think this would make a good interview question!

          And I thought this was fairly common knowledge! :laugh: (Not that that would preclude it from being an interview question, of course.) They "fixed" the behaviour for the foreach loop back in C# 5, but deliberately chose not to change the for loop:

          Closing over the loop variable considered harmful, part one | Fabulous adventures in coding[^]:

          UPDATE: We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The for loop will not be changed.


          "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

          M Offline
          M Offline
          Marc Clifton
          wrote on last edited by
          #4

          That's the follow-up interview question. "On what for loops does closure work?"

          Latest Articles:
          A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

          1 Reply Last reply
          0
          • M Marc Clifton

            I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

            for (int i=0; i <= 10; i++)
            {
            var task = Task.Run(async () =>
            {
            var convertResp = await StartTestConvert(i);
            ... other stuff
            });
            }

            And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

            for (int i=0; i <= 10; i++)
            {
            int n = i;
            var task = Task.Run(async () =>
            {
            var convertResp = await StartTestConvert(n);
            ... other stuff
            });
            }

            I think this would make a good interview question!

            Latest Articles:
            A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

            Sander RosselS Offline
            Sander RosselS Offline
            Sander Rossel
            wrote on last edited by
            #5

            Reminded me of The Deadlock Empire[^]. A game where you're a scheduler and have to break code by switching contexts :D You can play it for free in your browser, no registration or anything.

            Best, Sander Azure DevOps Succinctly (free eBook) Azure Serverless Succinctly (free eBook) Migrating Apps to the Cloud with Azure arrgh.js - Bringing LINQ to JavaScript

            1 Reply Last reply
            0
            • M Marc Clifton

              I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

              for (int i=0; i <= 10; i++)
              {
              var task = Task.Run(async () =>
              {
              var convertResp = await StartTestConvert(i);
              ... other stuff
              });
              }

              And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

              for (int i=0; i <= 10; i++)
              {
              int n = i;
              var task = Task.Run(async () =>
              {
              var convertResp = await StartTestConvert(n);
              ... other stuff
              });
              }

              I think this would make a good interview question!

              Latest Articles:
              A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

              J Offline
              J Offline
              jmaida
              wrote on last edited by
              #6

              I do not see the difference whether using i or n. Is async call creating a thread? Is this a C# thing? lambda? Closure? seems like a definition of global and local variable spaces. Confused

              "A little time, a little trouble, your better day" Badfinger

              S 1 Reply Last reply
              0
              • M Marc Clifton

                I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

                for (int i=0; i <= 10; i++)
                {
                var task = Task.Run(async () =>
                {
                var convertResp = await StartTestConvert(i);
                ... other stuff
                });
                }

                And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

                for (int i=0; i <= 10; i++)
                {
                int n = i;
                var task = Task.Run(async () =>
                {
                var convertResp = await StartTestConvert(n);
                ... other stuff
                });
                }

                I think this would make a good interview question!

                Latest Articles:
                A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

                J Offline
                J Offline
                Jeremy Falcon
                wrote on last edited by
                #7

                Marc Clifton wrote:

                I think this would make a good interview question!

                To mention the language that shall not be named (it rhymes with GuavaScript), closures was one of the ways to fake OOP before es6 came along with classes.

                function FakeClass() {
                // analogous to a protected variable
                const protectMe = 'When I grow up, I wanna be a class!';

                // with an anonymous function (kinda like a method)
                

                this.doSomething = function() {
                // "protectMe" will exist after this function is called
                console.log(protectMe);
                }
                }

                const sup = new FakeClass();

                console.log(sup.protectMe); // not defined
                sup.doSomething(); // prints "When I grow up, I wanna be a class!"

                These days I tend to use closures less and less with generators, promises, async/await, etc. There are just new ways to do the same thing in the language that shall not name be named. But, if you ever find yourself needing to access that lexical context after the parent function go bye bye (a static variable wouldn't quite cut it for that) and you don't want to clutter up your code with global/module crap... closures are a life saver. All-in-all, cool stuff.

                Jeremy Falcon

                J J 3 Replies Last reply
                0
                • M Marc Clifton

                  I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

                  for (int i=0; i <= 10; i++)
                  {
                  var task = Task.Run(async () =>
                  {
                  var convertResp = await StartTestConvert(i);
                  ... other stuff
                  });
                  }

                  And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

                  for (int i=0; i <= 10; i++)
                  {
                  int n = i;
                  var task = Task.Run(async () =>
                  {
                  var convertResp = await StartTestConvert(n);
                  ... other stuff
                  });
                  }

                  I think this would make a good interview question!

                  Latest Articles:
                  A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

                  E Offline
                  E Offline
                  englebart
                  wrote on last edited by
                  #8

                  That is why Java will only let you use _final_ “variables” for anonymous inner classes. Does StartTestConvert() receive by value/copy or by reference? ( Does it matter?) I would tend to wrap some of this into a class. I have moved in the direction of hiding threads/locks/async/await etc as much as possible from the client code. new Runner(i).launch();

                  1 Reply Last reply
                  0
                  • J Jeremy Falcon

                    Marc Clifton wrote:

                    I think this would make a good interview question!

                    To mention the language that shall not be named (it rhymes with GuavaScript), closures was one of the ways to fake OOP before es6 came along with classes.

                    function FakeClass() {
                    // analogous to a protected variable
                    const protectMe = 'When I grow up, I wanna be a class!';

                    // with an anonymous function (kinda like a method)
                    

                    this.doSomething = function() {
                    // "protectMe" will exist after this function is called
                    console.log(protectMe);
                    }
                    }

                    const sup = new FakeClass();

                    console.log(sup.protectMe); // not defined
                    sup.doSomething(); // prints "When I grow up, I wanna be a class!"

                    These days I tend to use closures less and less with generators, promises, async/await, etc. There are just new ways to do the same thing in the language that shall not name be named. But, if you ever find yourself needing to access that lexical context after the parent function go bye bye (a static variable wouldn't quite cut it for that) and you don't want to clutter up your code with global/module crap... closures are a life saver. All-in-all, cool stuff.

                    Jeremy Falcon

                    J Offline
                    J Offline
                    jmaida
                    wrote on last edited by
                    #9

                    lambdas/closures are used in "Lisp, C#, C++, Lua, Python, Ruby, JavaScript, Java, Excel or Google sheets" according to stack overflow, hence my unfamiliarity, being a lowly C guy. Thanx to CP, I have a new appreciation.

                    "A little time, a little trouble, your better day" Badfinger

                    J 1 Reply Last reply
                    0
                    • J Jeremy Falcon

                      Marc Clifton wrote:

                      I think this would make a good interview question!

                      To mention the language that shall not be named (it rhymes with GuavaScript), closures was one of the ways to fake OOP before es6 came along with classes.

                      function FakeClass() {
                      // analogous to a protected variable
                      const protectMe = 'When I grow up, I wanna be a class!';

                      // with an anonymous function (kinda like a method)
                      

                      this.doSomething = function() {
                      // "protectMe" will exist after this function is called
                      console.log(protectMe);
                      }
                      }

                      const sup = new FakeClass();

                      console.log(sup.protectMe); // not defined
                      sup.doSomething(); // prints "When I grow up, I wanna be a class!"

                      These days I tend to use closures less and less with generators, promises, async/await, etc. There are just new ways to do the same thing in the language that shall not name be named. But, if you ever find yourself needing to access that lexical context after the parent function go bye bye (a static variable wouldn't quite cut it for that) and you don't want to clutter up your code with global/module crap... closures are a life saver. All-in-all, cool stuff.

                      Jeremy Falcon

                      J Offline
                      J Offline
                      jmaida
                      wrote on last edited by
                      #10

                      thanx jeremy this helps

                      "A little time, a little trouble, your better day" Badfinger

                      J 1 Reply Last reply
                      0
                      • J Jeremy Falcon

                        Marc Clifton wrote:

                        I think this would make a good interview question!

                        To mention the language that shall not be named (it rhymes with GuavaScript), closures was one of the ways to fake OOP before es6 came along with classes.

                        function FakeClass() {
                        // analogous to a protected variable
                        const protectMe = 'When I grow up, I wanna be a class!';

                        // with an anonymous function (kinda like a method)
                        

                        this.doSomething = function() {
                        // "protectMe" will exist after this function is called
                        console.log(protectMe);
                        }
                        }

                        const sup = new FakeClass();

                        console.log(sup.protectMe); // not defined
                        sup.doSomething(); // prints "When I grow up, I wanna be a class!"

                        These days I tend to use closures less and less with generators, promises, async/await, etc. There are just new ways to do the same thing in the language that shall not name be named. But, if you ever find yourself needing to access that lexical context after the parent function go bye bye (a static variable wouldn't quite cut it for that) and you don't want to clutter up your code with global/module crap... closures are a life saver. All-in-all, cool stuff.

                        Jeremy Falcon

                        J Offline
                        J Offline
                        Jacquers
                        wrote on last edited by
                        #11

                        I thought you were kidding, but it actually exists :laugh: [GitHub - MaxwellWilliams/guavascript: CMSI 488 Compilers repository for Evan and Max's group project.](https://github.com/MaxwellWilliams/guavascript)

                        1 Reply Last reply
                        0
                        • J jmaida

                          lambdas/closures are used in "Lisp, C#, C++, Lua, Python, Ruby, JavaScript, Java, Excel or Google sheets" according to stack overflow, hence my unfamiliarity, being a lowly C guy. Thanx to CP, I have a new appreciation.

                          "A little time, a little trouble, your better day" Badfinger

                          J Offline
                          J Offline
                          Jeremy Falcon
                          wrote on last edited by
                          #12

                          C’s still an awesome language. Always will be. Can’t hurt to learn a second one though to be introduced to modern concepts. Some you can bring back to C, even if you have to implement them yourself. Either way, C rulez yo.

                          Jeremy Falcon

                          1 Reply Last reply
                          0
                          • J jmaida

                            thanx jeremy this helps

                            "A little time, a little trouble, your better day" Badfinger

                            J Offline
                            J Offline
                            Jeremy Falcon
                            wrote on last edited by
                            #13

                            You’re welcome man. You can actually do even more with closures too. Might type up some other examples tomorrow.

                            Jeremy Falcon

                            1 Reply Last reply
                            0
                            • M Marc Clifton

                              I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

                              for (int i=0; i <= 10; i++)
                              {
                              var task = Task.Run(async () =>
                              {
                              var convertResp = await StartTestConvert(i);
                              ... other stuff
                              });
                              }

                              And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

                              for (int i=0; i <= 10; i++)
                              {
                              int n = i;
                              var task = Task.Run(async () =>
                              {
                              var convertResp = await StartTestConvert(n);
                              ... other stuff
                              });
                              }

                              I think this would make a good interview question!

                              Latest Articles:
                              A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

                              P Offline
                              P Offline
                              Peter Adam
                              wrote on last edited by
                              #14

                              int n = i;

                              Amazing solution from an amazing language to cut the code clutter delivered to you by the Cool Kids on the Codeblock!

                              1 Reply Last reply
                              0
                              • M Marc Clifton

                                I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

                                for (int i=0; i <= 10; i++)
                                {
                                var task = Task.Run(async () =>
                                {
                                var convertResp = await StartTestConvert(i);
                                ... other stuff
                                });
                                }

                                And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

                                for (int i=0; i <= 10; i++)
                                {
                                int n = i;
                                var task = Task.Run(async () =>
                                {
                                var convertResp = await StartTestConvert(n);
                                ... other stuff
                                });
                                }

                                I think this would make a good interview question!

                                Latest Articles:
                                A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

                                F Offline
                                F Offline
                                Fly Gheorghe
                                wrote on last edited by
                                #15

                                From the good, old K&R C bible: A for that does not include a continue: for (e1; e2; e3) Block; is equivalent to: e1; while(e2) { Block; e3; } Replace e1 with (int i = 0;) and it will become clear what is the scope of i, and why it will have value 11. All subsequent languages like C++, java, C# etc. follows the same logic, since nobody dares to change what DMR (may God rest his soul in peace) has ruled.

                                1 Reply Last reply
                                0
                                • M Marc Clifton

                                  I love closures and lambdas, but I was quite amused when I wrote the following buggy code:

                                  for (int i=0; i <= 10; i++)
                                  {
                                  var task = Task.Run(async () =>
                                  {
                                  var convertResp = await StartTestConvert(i);
                                  ... other stuff
                                  });
                                  }

                                  And was wondering, how in the world does i get equal to 11!!! :laugh: Answer: bad closure. Fix:

                                  for (int i=0; i <= 10; i++)
                                  {
                                  int n = i;
                                  var task = Task.Run(async () =>
                                  {
                                  var convertResp = await StartTestConvert(n);
                                  ... other stuff
                                  });
                                  }

                                  I think this would make a good interview question!

                                  Latest Articles:
                                  A Lightweight Thread Safe In-Memory Keyed Generic Cache Collection Service A Dynamic Where Implementation for Entity Framework

                                  R Offline
                                  R Offline
                                  RooN3y
                                  wrote on last edited by
                                  #16

                                  ChatGPT explained this to me. I feel bad that I couldn't figure this one out :( Question to ChatGPT:

                                  In this example c# code "i" can equal 11. can you fix it?

                                  for (int i=0; i <= 10; i++)
                                  {
                                  var task = Task.Run(async () =>
                                  {
                                  var convertResp = await StartTestConvert(i);
                                  ... other stuff
                                  });
                                  }

                                  Response:

                                  Yes, in the provided code, the variable i is captured by the async lambda expression, which means by the time the async operations inside the lambda complete, the value of i may have changed. To fix this, you need to capture the loop variable correctly. Here's a way to do it:

                                  for (int i = 0; i <= 10; i++)
                                  {
                                  int current = i; // Capture the current value of i
                                  var task = Task.Run(async () =>
                                  {
                                  var convertResp = await StartTestConvert(current);
                                  // ... other stuff
                                  });
                                  }

                                  By capturing the loop variable inside the loop, you ensure that each async operation receives its own copy of the loop variable, preventing the issue where the value of i changes before the async operation completes.

                                  R 1 Reply Last reply
                                  0
                                  • R RooN3y

                                    ChatGPT explained this to me. I feel bad that I couldn't figure this one out :( Question to ChatGPT:

                                    In this example c# code "i" can equal 11. can you fix it?

                                    for (int i=0; i <= 10; i++)
                                    {
                                    var task = Task.Run(async () =>
                                    {
                                    var convertResp = await StartTestConvert(i);
                                    ... other stuff
                                    });
                                    }

                                    Response:

                                    Yes, in the provided code, the variable i is captured by the async lambda expression, which means by the time the async operations inside the lambda complete, the value of i may have changed. To fix this, you need to capture the loop variable correctly. Here's a way to do it:

                                    for (int i = 0; i <= 10; i++)
                                    {
                                    int current = i; // Capture the current value of i
                                    var task = Task.Run(async () =>
                                    {
                                    var convertResp = await StartTestConvert(current);
                                    // ... other stuff
                                    });
                                    }

                                    By capturing the loop variable inside the loop, you ensure that each async operation receives its own copy of the loop variable, preventing the issue where the value of i changes before the async operation completes.

                                    R Offline
                                    R Offline
                                    Rich Shealer
                                    wrote on last edited by
                                    #17

                                    I just had a conversation with ChatGPT about the topic. It was interesting that I was asking it questions about some specifics about what it was explaining and it did not fall over into a "I'm sorry, 2 plus 2 does equal 5" type of conversation. That has happened a lot to me in the past when trying to understand something.

                                    1 Reply Last reply
                                    0
                                    • J jmaida

                                      I do not see the difference whether using i or n. Is async call creating a thread? Is this a C# thing? lambda? Closure? seems like a definition of global and local variable spaces. Confused

                                      "A little time, a little trouble, your better day" Badfinger

                                      S Offline
                                      S Offline
                                      Stacy Dudovitz
                                      wrote on last edited by
                                      #18

                                      It has to do with capture, where we grab the value of i by the use of assignment into n before launching another task. There is nothing magical about using a variable named n... we could have called it george. What's important is that the current value of i in the for loop is captured before launching the task. If we had not done that, the for loop would complete execution, setting i to 11 before the first task was launched. That is what is meant by closure.

                                      1 Reply Last reply
                                      0
                                      Reply
                                      • Reply as topic
                                      Log in to reply
                                      • Oldest to Newest
                                      • Newest to Oldest
                                      • Most Votes


                                      • Login

                                      • Don't have an account? Register

                                      • Login or register to search.
                                      • First post
                                        Last post
                                      0
                                      • Categories
                                      • Recent
                                      • Tags
                                      • Popular
                                      • World
                                      • Users
                                      • Groups