Does a closure capture ref. to "this"?

Would a closure (that is only called delayed by a different object) capture a ref. to “this” if a local function is called? I thought so, but I am observing occasional issues “in the field” with this pattern (not precise code, just the pattern):

class test:MonoBehavior {

  public void doSth() {
  }

  void Start () {
      ActionQueue.ExecuteSometimeLater.Enqueue(() => {
               doSth();
       });  
  }
}

The test object itself, however, might go away before the closure is actually called. How would I be able to test within the closure whether “this” still exists at that point? What would be a valid test pattern for accessing a method on my former outer context? Would I need to explicitly capture “this” as a local var to have it kept until the closure is executed? AFAIK I can’t just go with “this?.doSth()”, as “this” would never be null when the closure executes, right? It might just no longer be the former object?

Thanks, habitoti

Actually I think you can because Unity objects are testable for null to see if they are destroyed.

Yes and no ^^. Of course this can never be really null. That’s something that the language doesn’t allow. So “this” will not be null, however since it represents a UnityEngine.Object the object can be “dead”. This state can not be checked with the conditional operator as it only works with true null checks. You would need to do a normal if statement (or use the ternary operator)

This should work:

ActionQueue.ExecuteOnMainThread.Enqueue(() => {
    if(this)
        doSth();
});
2 Likes

Interesting. TIL… I just checked and I would have thought that operator would reach to the same check for null that checking something against null would, which also works for Destroyed objects.

Well, I’ve never used that op… I am not big on polluting my code with new features whose only purpose is to cram more operations into one single line (as if that makes the world a better place!), just so that when that line fails (and it will!) I get to rewrite the code to figure out what’s really going on. (Chained Linq statements, I’m talking to you baby…)

But would „this“ at the point of execution of the closure not represent the transient closure class that‘s generated by the compiler, rather than the former game object?

It would but since this is an instance variable pointing to this particular instance of the class, it is as good as the copy of it bound into the closure.

Generally closure captures only matter if the original variable gets changed later, since C# does variable capture. Since the this is read-only, I don’t think you need concern yourself with capture.

No, you never really get any access to the hidden generated closure classes. Their functionality is hidden behind runtime logic, similar to “boxes” for boxed variables.

Well pretty much all of the fancy new implicit null checking operators do not work with Unity’s custom == operator as they all perform a direct ReferenceEquals check. So this is true for the ?? as well as ?. and ?[ ] operators.

1 Like