If you want to make a formal reply from events contained in the object to be GC collected?

Good day, there was a small issue.
A class, for example a Task. It contains the TaskComplete event to which anyone signed up. And there is a Ship class that contains a property of type Task CurrentTask (well or field does not matter). Suppose the task is completed and the CurrentTask is assigned a different Task instance or null. It is logical that the old instance will be some time assembled GC.
It seems to be obvious that will be all collected and there are no problems from the fact that the event was someone will not be signed (unlike the reverse, when you delete a subscriber, of course). But just in case, to clarify - do I understand correctly that to carry out a formal reply from the event, which will soon be collected by the GC, it makes no sense and it will not lead to memory leak?
July 12th 19 at 16:36
4 answers
July 12th 19 at 16:38
Solution
I tried to simulate your situation and it turned out that not evasion does not lead to a memory leak. I used the finalizer to track garbage collection. At the end of test approximately 14% of the objects are not destroyed, but calling GC.Collect() leads to their destruction. I interpret this so that the GC recognizes these objects as garbage, just haven't gotten to them yet. This is due to using a custom finalizer: for the destruction of such objects, the garbage collector needs more time. Read more about the event https://habrahabr.ru/post/89529/
class Program
{
 private static bool Unsubscribe = false;
 static void Main(string[] args)
{
 var ship = new Ship();
 Enumerable.Range(0, 100000).ToList().ForEach(i =>
{
 Console.Write($"\r{i + 1}\t");
 ship.MyTask = new MyTask(i);
 ship.MyTask.TaskCompleted += OnTaskCompleted;
ship.MyTask.DoAndRaise();
});

 ship.MyTask = null;

Console.WriteLine();
 Console.WriteLine("Raised:" + MyTask.EventsRaised);
 Console.WriteLine("Collected:" + MyTask.Collected);
GC.Collect();
GC.WaitForPendingFinalizers();
 Console.WriteLine("Collected:" + MyTask.Collected);
}

 static void OnTaskCompleted(object semder, int taskId)
{
 if (Unsubscribe)
{
 var mt = semder as MyTask;
 mt.TaskCompleted -= OnTaskCompleted;
}
}
}

public class MyTask
{
 public static int Collected, EventsRaised;
 public MyTask(int id)
{
 TaskId = id;
}

 public int TaskId { get; }

 public event EventHandler<int> TaskCompleted;

 public void DoAndRaise()
{
 TaskCompleted?.Invoke(this, TaskId);
 Interlocked.Increment(ref EventsRaised);
}

~MyTask()
{
 Interlocked.Increment(ref Collected);
}
}

public class Ship
{
 public MyTask MyTask { get; set; }
}</int>
July 12th 19 at 16:40
Solution
Is it necessary to explicitly remove event handler...
Answers By Jon Skeet.

.. The tricky case is when the publisher is long-lived but the subscribers don't want to be - in that case you need to unsubscribe the handlers..

If the source of the event will soon be harvested, he has no other links, and the subscriber only refers to the source, it is not necessary to unsubscribe.

I imagine it's like from the tree fell off the branch on which there are other branches with leaves, and inside they themselves can reference arbitrarily, but because there is no reference to the tree itself gave rise to them, such branch will be collected by GC.
July 12th 19 at 16:42
Thank you all for your answers now no uncertainty.
July 12th 19 at 16:44

Find more questions by tags Mono.NETC#