{"id":30233,"date":"2006-08-04T07:00:00","date_gmt":"2006-08-04T14:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2006\/08\/04\/the-implementation-of-anonymous-methods-in-c-and-its-consequences-part-3\/"},"modified":"2006-08-04T07:00:00","modified_gmt":"2006-08-04T14:00:00","slug":"the-implementation-of-anonymous-methods-in-c-and-its-consequences-part-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060804-00\/?p=30233","title":{"rendered":"The implementation of anonymous methods in C# and its consequences (part 3)"},"content":{"rendered":"<p>\nLast time we saw how the implementation details of anonymous\nmethods can make themselves visible when you start taking a\ndelegate apart by looking at its <code>Target<\/code>\nand <code>Method<\/code>.\nThis time, we&#8217;ll see how an innocuous code change can result in\ndisaster due to anonymous methods.\n<\/p>\n<p>\nOccasionally, I see people arguing over where local variables\nshould be declared.\nThe &#8220;decentralists&#8221; believe that variables should be declared\nas close to their point of first use as possible:\n<\/p>\n<pre>\nvoid MyFunc1()\n{\n ...\n for (int i = 0; i &lt; 10; i++) {\n  string s = i.ToString();\n  ...\n }\n ...\n}\n<\/pre>\n<p>\nOn the other hand,\nthe &#8220;consolidators&#8221; believe that local variables should be\ndeclared outside of loops.\n<\/p>\n<pre>\nvoid MyFunc2()\n{\n ...\n string s;\n for (int i = 0; i &lt; 10; i++) {\n  s = i.ToString();\n  ...\n }\n ...\n}\n<\/pre>\n<p>\nThe &#8220;consolidators&#8221; argue that hoisting the variable <code>s<\/code>\nmeans that the compiler only has to create the variable once,\nat function entry, rather than each time through the loop.\n<\/p>\n<p>\nAs a result, you can find yourself caught in a struggle between\nthe &#8220;decentralists&#8221; and the &#8220;consolidators&#8221; as members of\neach school touch a piece of code and &#8220;fix&#8221; the local\nvariable declarations to suit their style.\n<\/p>\n<p>\nAnd then there are the &#8220;peacemakers&#8221; who step in and say,\n&#8220;Look, it doesn&#8217;t matter. Can&#8217;t we all just get along?&#8221;\n<\/p>\n<p>\nWhile I admire the desire to have everyone get along,\nthe claim that it doesn&#8217;t matter is unfortunately not always true.\nLet&#8217;s stick some nasty code in where the dots are:\n<\/p>\n<pre>\ndelegate void MyDelegate();\nvoid MyFunc1()\n{\n <font COLOR=\"blue\">MyDelegate d = null;<\/font>\n for (int i = 0; i &lt; 10; i++) {\n  string s = i.ToString();\n  <font COLOR=\"blue\">d += delegate() {\n   System.Console.WriteLine(s);\n  };<\/font>\n }\n <font COLOR=\"blue\">d();<\/font>\n}\n<\/pre>\n<p>\nSince the <code>s<\/code> variable is declared inside the loop,\neach iteration of the loop gets its own copy of <code>s<\/code>,\nwhich means that each delegate\n<strong>gets its own copy of <code>s<\/code><\/strong>.\nThe first time through the loop, an <code>s<\/code> is created\nwith the value <code>\"0\"<\/code> and that <code>s<\/code> is used\nby the first delegate.\nThe second time through the loop, a new <code>s<\/code> is created\nwith the value <code>\"1\"<\/code>, and that new <code>s<\/code> is used\nby the second delegate.\nThe result of this code fragment is ten delegates, each of which\nprints a different number from 0 to 9.\n<\/p>\n<p>\nNow, a &#8220;consolidator&#8221; looks at this code and says,\n&#8220;How inefficient, creating a new <code>s<\/code> each time through\nthe loop. I shall hoist it and bask in the accolades of my countrymen.&#8221;\n<\/p>\n<pre>\ndelegate void MyDelegate();\nvoid MyFunc2()\n{\n MyDelegate d = null;\n <font COLOR=\"blue\">string s;<\/font>\n for (int i = 0; i &lt; 10; i++) {\n  <font COLOR=\"blue\">s<\/font> = i.ToString();\n  d += delegate() {\n   System.Console.WriteLine(s);\n  };\n }\n d();\n}\n<\/pre>\n<p>\nIf you run this fragment, you get different behavior.\nA single <code>s<\/code> variable is created for all the\nloop iterations to share.\nThe first time through the loop, the value of <code>s<\/code> is\n<code>\"0\"<\/code>, and then the first delegate is created.\nThe second loop iteration\nchanges the value of <code>s<\/code> to <code>\"1\"<\/code>\nbefore creating the second delegate.\nRepeat for the remaining eight delegates, and at the end of\nthe loop, the value of <code>s<\/code> is <code>\"9\"<\/code>,\nand ten delegates have been added to <code>d<\/code>.\nWhen <code>d<\/code> is invoked, all the delegates\nprint the value of the <code>s<\/code>\nvariable, which they are sharing and which has the value <code>\"9\"<\/code>.\nThe result:\n<code>9<\/code> is printed ten times.\n<\/p>\n<p>\nNow, I happen to have constructed this scenario to make the\n&#8220;consolidators&#8221; look bad, but I could also have written it\nto make the &#8220;decentralists&#8221; look bad for pushing a variable\ndeclaration into a loop scope when it should have remained outside.\n(All you have to do is read the above scenario in reverse.)\n<\/p>\n<p>\nThe point of this little exercise is that when\na &#8220;consolidator&#8221; or a &#8220;decentralist&#8221;\ngoes through an entire program &#8220;tuning up&#8221;\nthe declarations of local variables,\nthe result can be a broken program,\neven though the person making the change was convinced\nthat their change\n&#8220;had no effect; I was just making the code prettier \/ more efficient&#8221;.\n<\/p>\n<p>\nWhat&#8217;s the conclusion here?\n<\/p>\n<p>\nWrite what you mean and mean what you write.\nIf the precise scope of a variable is important,\nmake sure to comment it as such so that somebody won&#8217;t\nmess it up in a &#8220;clean-up&#8221; pass over your program.\nIf there are two ways of writing the same thing,\nthen write the one that is more maintainable.\nAnd if you feel that one method is superior from a performance point of view,\nthen (1)&nbsp;make sure you&#8217;re right, and (2)&nbsp;make sure it matters.\n<\/p>\n<p>\n<b>Update<\/b>:\nIn C# 5, the rules for the <code>foreach<\/code> statement\n<a HREF=\"https:\/\/ericlippert.com\/2009\/11\/16\/closing-over-the-loop-variable-considered-harmful-part-two\/\">\nchanged in a way that affects lambda capture<\/a>:\nThe control variable of the <code>foreach<\/code> is now scoped to the\nloop body,\nwhich means that capturing it in a lambda captures the current iteration,\nbecause each iteration gets a separate copy of th3e variable.\nThat doesn&#8217;t affect our <code>for<\/code> loop above, but it is\nworth calling out.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How local variables are captured.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-30233","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>How local variables are captured.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/30233","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=30233"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/30233\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=30233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=30233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=30233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}