Я попытался смоделировать вашу ситуацию и получилось, что не отписка не приводит к утечке памяти. Я использовал финализатор, чтобы отследить сборку мусора. В конце теста примерно 14% объектов не уничтожено, но вызов GC.Collect() приводит к их уничтожению. Я это интерпретирую так, что GC распознает эти объекты как мусор, просто еще не добрался до них. Это связано и с использованием кастомного финализатора: для уничтожения таких объектов сборщику мусора нужно больше времени. Подробнее про события
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; }
}