{"id":96625,"date":"2017-07-17T07:00:00","date_gmt":"2017-07-17T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=96625"},"modified":"2019-03-13T01:14:10","modified_gmt":"2019-03-13T08:14:10","slug":"20170717-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170717-00\/?p=96625","title":{"rendered":"Revisions to previous discussion of the implementation of anonymous methods in C#"},"content":{"rendered":"<p>Welcome to CLR Week. <\/p>\n<p>Yes, it&#8217;s been a long time since the last CLR Week. Some people might consider that a feature. <\/p>\n<p>Anyway, I&#8217;m going to start by calling attention to some revisions to previous discussion of the implementation of anonymous methods in C#. <\/p>\n<ul>\n<li>    <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20060802-00\/?p=30263\">Part 1<\/a> <\/li>\n<li>    <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/\">Part 2<\/a> <\/li>\n<li>    <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20060804-00\/?p=30233\">Part 3<\/a> <\/li>\n<\/ul>\n<p>The first revision is one most people are well aware of, namely that <a HREF=\"https:\/\/ericlippert.com\/2009\/11\/16\/closing-over-the-loop-variable-considered-harmful-part-two\/\">the scope of the control variable of a <code>foreach<\/code> statement is now the controlled statement<\/a>. What this means for you is that closing over the loop control variable of a <code>foreach<\/code> statement is not dangerous. Note, however, that closing over the loop control variable of a <code>for<\/code> statement is still dangerous. <\/p>\n<p>The second revision is that noncapturing lambdas are no longer wrappers around a static method. Even if the lambda captures nothing, it is still converted to an instance method (of an anonymous type). <\/p>\n<p>The reason <a HREF=\"http:\/\/stackoverflow.com\/q\/30897647\/#comment49837759_30897727\">given by Kevin Pilch-Bisson<\/a> is that &#8220;delegate invokes are optimized for instance methods and have space on the stack for them. To call a static method they have to shift parameters around.&#8221; <\/p>\n<p>Let&#8217;s unpack that explanation. <\/p>\n<p>Recall that instance methods have a hidden <code>this<\/code> parameter, whereas static methods do not. Suppose you want to forward a call from one method to another. For concreteness, let&#8217;s say you have <\/p>\n<pre>\nclass C1\n{\n public void M1(int x, int y, int z)\n {\n  System.Console.WriteLine(\"From {0} to {1} via {2}\", x, y, z);\n }\n static public void S1(int x, int y, int z)\n {\n  System.Console.WriteLine(\"From {0} to {1} via {2}\", x, y, z);\n }\n}\n\nclass C2\n{\n private C1 c1 = new C1();\n static private C1 s1 = new C1();\n\n public void M2(int x, int y, int z)\n {\n  c1.M1(x, y, z);\n }\n static public void S2(int x, int y, int z)\n {\n  C1.S1(x, y, z);\n }\n public void M2S(int x, int y, int z)\n {\n  C1.S1(x, y, z);\n }\n static public void S2M(int x, int y, int z)\n {\n  s1.M1(x, y, z);\n }\n}\n<\/pre>\n<p>Since the layouts for the parameters to both <code>C1.M1()<\/code> and <code>C2.M2()<\/code> method match, <code>C2.M2()<\/code> can get away with the following: <\/p>\n<ul>\n<li>    Fetch <code>this.c1<\/code>. <\/li>\n<li>    Validate that the fetched value is not null. <\/li>\n<li>    Replace <code>this<\/code> with the fetched value. <\/li>\n<li>    Jump to <code>C1.M1<\/code>. <\/li>\n<\/ul>\n<p>The assembly for <code>C2.M2<\/code> on x86 would go something like this: <\/p>\n<pre>\n; <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20040108-00\/?p=41163\">fastcall convention<\/a> passes\n; the first parameter (this) in ecx\n; the second parameter (x) in edx\n; remaining parameters (y, z) on the stack\n\nC2.M2:\n    mov  ecx, [ecx].c1  ; fetch this.c1\n    cmp  ecx, [ecx]     ; <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20070816-00\/?p=25553\">null check<\/a>\n    jmp  C1.M1          ; all the other parameters are already set\n<\/pre>\n<p>Similarly, forwarding a call from one static method to another can reuse the stack frame as-is: <\/p>\n<pre>\nC2.S2:\n    jmp C1.S1           ; all parameters are already set properly\n<\/pre>\n<p>However, forwarding from an instance method to a static method or vice versa isn&#8217;t so easy. The compiler would either have to generate a traditional non-tail call: <\/p>\n<pre>\nC2.M2S:\n    mov  ecx, edx       ; put x into ecx\n    mov  edx, [esp][4]  ; put y into edx\n    push edx, [esp][8]  ; push z\n    call C1.S1\n    ret  8\n\nC2.S2M:\n    push [esp][4]       ; push z\n    push edx            ; push y\n    mov  edx, ecx       ; put x into edx\n    mov  ecx, [C2.s1]   ; put C2.s1 into ecx\n    cmp  ecx, [ecx]     ; <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20070816-00\/?p=25553\">null check<\/a>\n    call C1.M1          ; call it\n    ret  8\n<\/pre>\n<p>Or maybe the compiler plays funny stack rewriting games:&sup1; <\/p>\n<pre>\nC2.M2S:\n    mov  ecx, edx       ; put x into ecx\n    pop  eax            ; pop return address\n    pop  edx            ; pop y into edx\n                        ; leave z on the stack\n    push eax            ; restore return address\n    jmp  C1.S1\n\nC2.S2M:\n    pop  eax            ; pop return address\n    push edx            ; push y\n    push eax            ; restore return address\n    mov  edx, ecx       ; put x into edx\n    mov  ecx, [C2.s1]   ; put C2.s1 into ecx\n    cmp ecx, [ecx]      ; <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20070816-00\/?p=25553\">null check<\/a>\n    jmp C1.M1\n<\/pre>\n<p>Both of these are worse than the case where the call is forwarded to a function of matching ilk. <\/p>\n<p>Since delegate invoke is done instance-style, the code to dispatch the delegate to the lambda is more efficient if the lambda is also instance. <\/p>\n<p>Since the language specification does not specify the nature of the lambda, whether the delegate represents a static or instance method is an implementation detail that can change at any time. <\/p>\n<p>And it did. <\/p>\n<p>&sup1; Note that these stack rewriting games are not available to x64 because of alignment requirements. On x64, we are forced to generate a traditional non-tail call. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>All instance all the time.<\/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-96625","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>All instance all the time.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96625","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=96625"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96625\/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=96625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=96625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=96625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}