Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

does passing a method of one object to another object keep the first object alive?

Suppose I have three objects: 'a', 'b' and 'c'. Object 'a' and 'c' are long-lived, statically referenced service singletons. Object 'b' is short-lived, i.e. no static references keep it alive.

Now suppose object 'a' creates an instance of object 'b' in the scope of one of its methods, e.g.

B b = new B();

Further suppose that the class B looks something like this:

public B ()
{
    C.ActionList.Add ( SomeMethod );
}

void SomeMethod ()
{
...
}

Now, how long does object 'b' live? My presumption is that it lives beyond the scope of the method that called its constructor; specifically, for as long as its method is still in the 'ActionList' of object 'c'.

Is that correct? If not, and it gets garbage collected, what happens when 'c' runs all the methods in its 'ActionList'?

Bonus question: What if the method on 'b' is not named, but anonymous and written in the constructor as follows:

public B ()
{
    C.ActionList.Add ( () => {
    ...
    } );
}
like image 604
nerophon Avatar asked Apr 11 '14 19:04

nerophon


1 Answers

specifically, for as long as its method is still in the 'ActionList' of object 'c'.

Yes, that is correct. A delegate to an instance method creates a "hard reference" to the instance itself, and will keep it alive. Your second question is not relevant.

Note that this is why event subscriptions are a common source of "memory leaks" in .NET - they don't technically leak, but event subscriptions are based on delegates, and have this same behavior, so an event subscription holds a reference to the instance.

What if the method on 'b' is not named, but anonymous and written in a lambda in the constructor?

This is the same. If the lambda uses state of the instance in question, it will get turned into an instance method on a type with a reference to that instance by the compiler, and hold a reference. (Note that it's possible for some lambda expressions to be turned into static methods, if they don't rely on any closed over values, the instance, etc, in which case it would not hold a reference.)

In your case, the contents of the ... would determine this. If your expression was nothing but: () => { Console.WriteLine("Foo"); }, it wouldn't need to close over any values in the instance, and wouldn't use the instance in question, so it would not hold a reference. If you do () => { Console.WriteLine(this.Foo); }, however, it would create a method on a type with a reference to this, and keep the class instance alive.

like image 166
Reed Copsey Avatar answered Nov 10 '22 00:11

Reed Copsey