Saturday 21 January 2017

Closures and this

The other day, when reminded of the fact that ES6 arrow functions trap the lexical "this" rather than using a dynamic "this" (one can think of them as bound functions, though I think they are not exactly the same), I tried to refresh my mind on the internals of how C# anonymous methods trap "this" (it's trapped, so it's a lexical "this" rather than dynamic), and it took me to some interesting new finding.

Let's say you write an anonymous method using some of the external local variables and "this". We need to closure on them, to trap them.

public class Person
{
 public string Name{get;set;}
 
 public void DoSomethingWithThisAndFreeVar()
 {
  var counter = 0;
  //this closure traps both a variable and "this". Its method will get created in a separate, auxiliar class, with a __this and counter fields
  Action f1 = () => {
   counter++;
   this.Name += ".";
  };
  f1();
 }
}

In this case the smart compiler will create a method in an auxiliar class that will have fields supporting the local variables, and a __this field pointing to the "this" field of our normal class. This is what I remembered and it's clear.

Now let's say we have an anonymous method that only makes use of "this".

public class Animal
{
 public string Name{get;set;}
 
 public void DoSomethingWithThis()
 {
  //this closure only uses "this", so in this case its method will get created inside this class itself
  Action f1 = () => { 
   this.Name += ".";
  };
  f1();
 }
}

As we know, when a delegate is created it gets a Target property that points to the instance of the class where the method for the delegate is created ("this"). So, in this case we don't need an auxiliar class, the method for the anonymous method can be created just inside our normal class. It makes sense to think that the C# compiler will distinguish between the previous situation and this one and emit different code. And of course that's right as we can see.

Maybe one could think why in the first case the compiler does not do the same. Put the method in the existing class and add auxiliar fields for the local variables. That would make no sense at all. First we would be adding a lot of rubbish data to the class, data that would be around taking memory as long as the object exists, while the auxiliar classes for the closure are usually short lived. Second, we could have multiple instances of that closure at the same time, that would want to share the "this" but trap their own local variables, so obviously we can not have them as fields in the main class.

No comments:

Post a Comment