Instead of using a delegate, the subscriber can implement an interface ISubscriber
that has a method SomeEvent
, and pass itself to the publisher (to a method with a signature Subscribe(ISubscriber subscriber)
). The publisher then will store this reference to the subscriber and call subscriber.SomeEvent
when necessary.
Something like:
public interface ISubscriber
{
void SomeEvent(object publisher, object data);
}
public class SomePublisher
{
private readonly HashSet<ISubscriber> subscribers = new HashSet<ISubscriber>();
public void Subscribe(ISubscriber subscriber)
{
subscribers.Add(subscriber);
}
public void Unsubscribe(ISubscriber subscriber)
{
subscribers.Remove(subscriber);
}
public void DoSomething()
{
// Do something
// Then
foreach (var subscriber in subscribers)
{
object data = "Some data";
subscriber.SomeEvent(this, data);
}
}
}
Note that this model of publisher/subscriber isn't limited to a single "event": ISubscriber
could have multiple methods corresponding on multiple "events". The only problem is that if there are multiple "events" in the interface, the subscriber must "subscribe" to all the events (must have methods for all the events). So if there are an OnAdded
and an OnRemoved
methods in ISubscriber
, classes implementing ISubscriber
must have both methods (clearly they could be empty stubs that do nothing)
I'll add that, in the end, delegates can be "simulated" by interfaces with a single method, and events can be considered to be List<somedelegatetype>
, so events can be considered to be List<ISomeInterface>
. Java for example doesn't have delegates and uses interfaces with a single method in lieu of them (see for example Java Delegates?)