What is alternative of Events and Delegates in c#?
Asked Answered
P

2

7

I have been recently asked in an interview like if we don't have Events and Delegates how can we acheive the same functionality of Publisher and Subsriber model without Delegate and Events.

Can you please explain me with an example it will be really helpful and will help others too?

Prudie answered 5/5, 2017 at 8:45 Comment(0)
F
11

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?)

Fetation answered 5/5, 2017 at 8:54 Comment(1)
Thanks@Fetation this example is really helpful and that i was looking forPrudie
R
9

The simplest implementation of Publisher / Subscriber:

// Universal interface for all subscribers:
public interface ISubscriber<TEvent>
{
    void HandleEvent(object sender, TEvent ev);
}

// Universal publisher, can be used directly with generic argument
public class Publisher<TEvent>
{
    protected ISet<ISubscriber<TEvent>> _subscribers = new HashSet<ISubscriber<TEvent>>();

    public void Publish(TEvent ev)
    {
        foreach (var sub in _subscribers)
        {
            sub.HandleEvent(this, ev);
        }
    }

    public void Subscribe(ISubscriber<TEvent> subscriber)
    {
        _subscribers.Add(subscriber);
    }

    public void Unsubscribe(ISubscriber<TEvent> subscriber)
    {
        _subscribers.Remove(subscriber);
    }
}

// Or can be inherited to encapsulate any sort of logic
public class RandomIntegerPublisher : Publisher<int>
{
    private readonly Random _random = new Random();

    public void Publish()
    {
        Publish(_random.Next());
    }
}

// Example subscriber, which can even implement multiple ISubscriber interfaces
public class ExampleSubscriber : ISubscriber<int>, ISubscriber<string>
{
    public void HandleEvent(object sender, int ev)
    {
        Console.WriteLine($"Integer event: {ev}");
    }

    public void HandleEvent(object sender, string ev)
    {
        Console.WriteLine($"String event: {ev}");
    }
}

void Main()
{
    var subscriber = new ExampleSubscriber();

    var randomIntegerPublisher = new RandomIntegerPublisher();
    randomIntegerPublisher.Subscribe(subscriber);

    var stringPublisher = new Publisher<string>();
    stringPublisher.Subscribe(subscriber);

    randomIntegerPublisher.Publish();
    randomIntegerPublisher.Publish();
    randomIntegerPublisher.Publish();
    stringPublisher.Publish("Hello World!");
}

Outputs:

Integer event: 1547238746
Integer event: 844169413
Integer event: 673377792
String event: Hello World!

It is not very flexible since uses OOP for publisher / subscriber pattern implementation and required classes to be created for every type of publisher and every specific subscriber, but it shows the main idea, which can be improved by yourself in many ways.

Ripplet answered 5/5, 2017 at 9:2 Comment(1)
Thanks Yeldar for your solution it is really helpful .I don't have enough points to upvote your solutionPrudie

© 2022 - 2024 — McMap. All rights reserved.