Back to Basic – Events in C#

Back to Basic – Events in C#

In order to understand Events, let’s first understand what Publisher Subscriber model is:

According to the Wikipedia publisher subscriber model is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers. Instead, published messages without knowledge of what, if any, subscribers there may be. Similarly, subscribers express interest in receiving messages that are of interest, without knowledge of what, if any, publishers there are.

For example a publisher of a newspaper is not directly aware of who its reader are. Their job is to publish the newspaper (on an average knowing the quantity, but not who those readers in specific). In the same manner the subscriber of the newspaper is not aware of how, when and where that newspaper publishes all what they know is they want to read the newspaper post, it’s is published, they can also opt to subscribe to some other newspaper on unsubscribe to all newspapers, in short neither the publisher not the subscriber is aware of each other.

pubsubchannel

Let write a sample code for publish–subscribe model in which where are going to extend our previous example of multicast delegate (Back To Basic – Delegate in C# ). The publisher reads some numbers and publish if the number is even. While we have two subscribers who wants to listen to even numbers and write/print them in a file or console.

public delegate void EvenDelegate(int number);
public class Publisher
{
public EvenDelegate EvenDelegate {get; set;}
public void Publish()
{
for (int i = 1; i < 11; i++)
{
if (i % 2 == 0)
{
EvenDelegate(i);
}
}
}
}

private class SubscriberConsole
{
public void Subscribe(ref EvenDelegate evenDelegate)
{
if (evenDelegate == null)
{
evenDelegate = WriteToConsole;
}
else
{
evenDelegate += WriteToConsole;
}
}

public void WriteToConsole(int number)
{
Console.WriteLine("The number is - {0}", number);
}
}

public class SubscriberFile
{
public void Subscribe(ref EvenDelegate evenDelegate)
{
if (evenDelegate == null)
{
evenDelegate = PrintIntoFile;
}
else
{
evenDelegate += PrintIntoFile;
}
}

public void PrintIntoFile(int number)
{
using (var fileStream = new FileStream(@"c:\demo\file.txt", FileMode.Append, FileAccess.Write))
{
using (var streamWriter = new StreamWriter(fileStream))
{
streamWriter.WriteLine("The number is - {0}", number);
}
}
}
}

 

public static void Main(string[] args)
{
var publisher = new Publisher();
var subscriberConsole = new SubscriberConsole();
var subscriberFile = new SubscriberFile();
subscriberConsole.Subscribe(ref publisher.EvenDelegate);
subscriberFile.Subscribe(ref publisher.EvenDelegate);
publisher.Publish();
}

 Related Post  : Back To Basic – Delegate in C#

The publisher has a property of the EvenDelegate and a method publish where it publishes when it encounters an even number.

While both the subscribers have a method called subscribe where they get their methods WriteToConsole and PrintIntoFile added to the invocation list of the EvenDelegate.

The shortcoming to the above code is that subscribers have full access to the delegate which means they are capable of adding more subscribers or removing an existing subscriber without that subscriber’s knowledge, which is bad. This problem is called as Naked Delegate Exposure.

Diagram1

Now the first things which come to our mind is how to solve this problem, it very clear from the problem is that delegates should not be exposed to the subscribers which means if we need to encapsulate the delegate from subscribers to avoid them to make any changes, this encapsulation is called Events.

eventdelegate

So let’s see how we can solve this problem using Events:

A Delegate which needs to be encapsulated by Event must be of return type void and in general has two parameters, first the sender of the event and second the arguments which need to be passed when the event is fired.

So let’s see firstly how to create an event argument.

Event arguments must be derived from the class EventArgs and should have properties which needs to be passed when the event is raised. In our case we need to pass the number, hence our class NumberEventArgs will be

 


public class NumberEventArgs : EventArgs

{

public int Number { get; set; }

 

public NumberEventArgs(int num)

{

this.Number = num;

}

}

 

Next step is to define a delegate which takes two parameters the sender and the event argument and return type is void.


public delegate void EvenDelegate(object sender, NumberEventArgs args);

 

Now we need to create an event for our Delegate

public event EvenDelegate EvenHandler;

Now we need to can the case when the event is fired, in which we check if the event is not null we invoke it.

public void EvnetFired(NumberEventArgs args)
{
if (EvenHandler != null)
{
EvenHandler(this, args);
}
}

Now the publisher is ready to publish the event, so whenever an even number it found the event get published.

public void Publish()
{
for (int i = 1; i < 11; i++)
{
if (i % 2 == 0)
{
EvenHandler(this, new NumberEventArgs(i));
}
}
}

So let’s see the entire code in total now


public delegate void EvenDelegate(object sender, NumberEventArgs args);
public class NumberEventArgs : EventArgs

{

public int Number { get; set; }

public NumberEventArgs(int num)
{
this.Number = num;
}
}

public class Publisher

{
public event EvenDelegate EvenHandler;
public void EvnetFired(NumberEventArgs args)
{
if (EvenHandler != null)
{
EvenHandler(this, args);
}
}

public void Publish()
{
for (int i = 1; i < 11; i++)
{
if (i % 2 == 0)
{
EvenHandler(this, new NumberEventArgs(i));
}
}
}
}

private class SubscriberConsole
{
public void WriteToConsole(object sender, NumberEventArgs args)
{
Console.WriteLine("The number is - {0}", args.Number);
}
}

public class SubscriberFile
{
public void PrintIntoFile(object sender, NumberEventArgs args)
{
using (var fileStream = new FileStream(@"c:\demo\file.txt", FileMode.Append, FileAccess.Write))
{
using (var streamWriter = new StreamWriter(fileStream))
{
streamWriter.WriteLine("The number is - {0}", args.Number);
}
}
}
}

public static void Main(string[] args)
{
var publisher = new Publisher();
var subscriberConsole = new SubscriberConsole();
var subscriberFile = new SubscriberFile();
publisher.EvenHandler += subscriberConsole.WriteToConsole;
publisher.EvenHandler += subscriberFile.PrintIntoFile;
publisher.Publish();
}

So as you see a publisher has no idea who all its subscriber are all what it is responsible is for publishing event, same way a subscriber has no information about the publisher and neither it has any access to the invocation list, a subscriber now only need to expose the method which needs to listen to the published event.

Shashank Bisen

Shashank Bisen is curious blogger and speaker who love to share his knowledge and experience which can help anyone in their day to day work. 

2 Comments to “Back to Basic – Events in C#”

  1. Manish

    Once again nice article. Keep posting in the community.
    But some correction required in first example in Publisher class where you have taken
    ////public EvenDelegate EvenDelegate {get; set;}
    i think it should not be a property because we can’t refer property.
    And second point why you have taken //EvnetFired(NumberEventArgs args)?

Comments are closed.