No seu caso, está tudo bem. É o objeto que publica os eventos que mantém os alvos dos manipuladores de eventos ativos. Então, se eu tiver:
publisher.SomeEvent += target.DoSomething;
então publisher
tem uma referência, target
mas não o contrário.
No seu caso, o editor será elegível para a coleta de lixo (supondo que não haja outras referências a ele), portanto, o fato de ter uma referência aos destinos do manipulador de eventos é irrelevante.
O caso complicado é quando o editor tem vida longa, mas os assinantes não querem, nesse caso, é necessário cancelar a inscrição dos manipuladores. Por exemplo, suponha que você tenha algum serviço de transferência de dados que permita assinar notificações assíncronas sobre alterações na largura de banda e que o objeto do serviço de transferência tenha vida longa. Se fizermos isso:
BandwidthUI ui = new BandwidthUI();
transferService.BandwidthChanged += ui.HandleBandwidthChange;
// Suppose this blocks until the transfer is complete
transferService.Transfer(source, destination);
// We now have to unsusbcribe from the event
transferService.BandwidthChanged -= ui.HandleBandwidthChange;
(Na verdade, você deseja usar um bloco finalmente para garantir que você não vaze o manipulador de eventos.) Se não cancelássemos a inscrição, eles BandwidthUI
permaneceriam pelo menos enquanto o serviço de transferência.
Pessoalmente, raramente me deparo com isso - geralmente, se eu assino um evento, o destino desse evento dura pelo menos o tempo que o editor - um formulário dura o mesmo que o botão que está nele, por exemplo. Vale a pena conhecer esse possível problema, mas acho que algumas pessoas se preocupam com isso quando não precisam, porque não sabem para que lado as referências vão.
EDIT: Isso é para responder ao comentário de Jonathan Dickinson. Primeiro, observe os documentos para Delegate.Equals (object) que claramente fornecem o comportamento de igualdade.
Em segundo lugar, aqui está um programa curto, mas completo, para mostrar o funcionamento da desinscrição:
using System;
public class Publisher
{
public event EventHandler Foo;
public void RaiseFoo()
{
Console.WriteLine("Raising Foo");
EventHandler handler = Foo;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
else
{
Console.WriteLine("No handlers");
}
}
}
public class Subscriber
{
public void FooHandler(object sender, EventArgs e)
{
Console.WriteLine("Subscriber.FooHandler()");
}
}
public class Test
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
publisher.Foo += subscriber.FooHandler;
publisher.RaiseFoo();
publisher.Foo -= subscriber.FooHandler;
publisher.RaiseFoo();
}
}
Resultados:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Testado em Mono e .NET 3.5SP1.)
Edição adicional:
Isso serve para provar que um editor de eventos pode ser coletado enquanto ainda há referências a um assinante.
using System;
public class Publisher
{
~Publisher()
{
Console.WriteLine("~Publisher");
Console.WriteLine("Foo==null ? {0}", Foo == null);
}
public event EventHandler Foo;
}
public class Subscriber
{
~Subscriber()
{
Console.WriteLine("~Subscriber");
}
public void FooHandler(object sender, EventArgs e) {}
}
public class Test
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
publisher.Foo += subscriber.FooHandler;
Console.WriteLine("No more refs to publisher, "
+ "but subscriber is alive");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End of Main method. Subscriber is about to "
+ "become eligible for collection");
GC.KeepAlive(subscriber);
}
}
Resultados (no .NET 3.5SP1; o Mono parece se comportar de maneira um pouco estranha aqui. Iremos analisar isso em algum momento):
No more refs to publisher, but subscriber is alive
~Publisher
Foo==null ? False
End of Main method. Subscriber is about to become eligible for collection
~Subscriber