Creating an Action
instance from a MethodGroup creates an instance that isn't attached in any way to the instance of the class that the method is a member of. That means that there is nothing keeping your Action
rooted, and the GC will happily collect it.
If you store a reference to the Action
as a member of the class, it will become rooted to the lifetime of that instance, preventing it from being collected as long as that instance is alive.
public class SomeClass
{
public SomeClass()
{
DoSomething = this.DoSomething_Internal ;
}
public Action DoSomething { get; }
private void DoSomething_Internal() { }
}
Note: I made the original DoSomething
private, and renamed it to DoSomething_Internal
, replacing the old DoSomething
with a readonly property in order to keep the class signature as close as possible to your original class. You don't need to follow this pattern exactly, any reference to the Action
stored in the class, including in a normal field, will do. Though you will still have to expose that reference somehow if you actually want to be able to use it.
You can test it like this:
var inst = new SomeClass();
var weakRef = new WeakReference<Action>(inst.DoSomething);
GC.Collect();
GC.WaitForPendingFinalizers(); // You should do this after forcing a GC, just in case there is still GC work being done in the background.
Console.WriteLine($"inst is alive = {inst != null} : weakRef.Target is alive = {weakRef.TryGetTarget(out Action callback1)}");
// These next 2 lines discard local variables, as Hans points out in the comments,
// DO NOT do this in production code. Please read the link he posted for more details.
inst = null; // discard the class instance
callback1 = null; // discard the temporary Action instance from TryGetTarget, otherwise it will act as a GC Root, preventing it from being collected later.
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine($"inst is alive = {inst != null} : weakRef.Target is alive = {weakRef.TryGetTarget(out Action callback2)}");
Which produces the following output:
inst is alive = True : weakRef.Target is alive = True
inst is alive = False : weakRef.Target is alive = False