Events, delegates and lambdas in .NET C# part 6: other scenarios

Introduction

So far in this series we’ve seen the basics of events, delegates and lambdas. We’ve looked at examples of how to define these elements and use them in code.

It’s now time for us to look at some other simulated scenarios where you can use these language elements. In particular we’ll look at the following:

  • Mediator pattern with delegates
  • Asynchronous delegate invocation

Communication across components

Imagine a scenario where you have a dynamic GUI of a Windows application. The components – widgets – can be loaded here and there and they are independent of each other to allow for loose coupling. At the same time they should be able to communicate with each other.

This scenario can be generalised: imagine independent objects that don’t know about other objects but still want to communicate with them in case they are present.

This is a perfect scenario for events and delegates and a pattern called the Mediator pattern. If you don’t know what a mediator is then make sure to read that introduction where the pattern is demonstrated without using events and delegates. With independent components we’ll need a Mediator that acts as a glue for those components. They will only know about the Mediator but not the other objects they are indirectly communicating with.

An additional pattern that is useful in this scenario is the Singleton pattern. If you don’t know what it means then skim through that article to get the basics. The purpose of the singleton in this case is to have only one Mediator class in memory. Otherwise the independent components may reference different mediators.

Note that this post concentrates on the role of delegates and events. If you’d like to extend this example so that it better follows SOLID and abstractions then look through blog entries in the Architecture and patterns section.

Open the project we’ve been working on in this series and add a new C# library called ComponentsMediator. Add a new class to it called Mediator which will be responsible for the communication between the independent components.

Add the following elements to adhere to the singleton pattern. We’ll employ a simple version of the pattern. Read the referenced article to see what some of the other possible solutions are.

private static readonly Mediator _instance = new Mediator();

private Mediator() { }
public static Mediator Instance
{
        get
	{
		return _instance;
	}
}

We’ll simulate the scenario where the user selects a product in a drop down list. We want to update the screen accordingly. Add the following Product class to the project:

public class Product
{
	public int Id { get; set; }
	public string Name { get; set; }
	public DateTime Registered { get; set; }
	public int OnStock { get; set; }
}

Return to Mediator.cs and add the following event handler to the Mediator:

public event EventHandler<ProductChangedEventArgs> ProductChanged;

…where ProductChangedEventArgs looks as follows:

public class ProductChangedEventArgs : EventArgs
{
	public Product Product { get; set; }
}

We’ll raise the event in the following method:

public void OnProductChanged(object sender, Product product)
{
	if (ProductChanged != null)
	{
		ProductChanged(sender, new ProductChangedEventArgs() { Product = product });
	}
}

By allowing the sender to be passed in we show that the original event raiser was an object different from the Mediator. Otherwise all products would “think” that the Mediator raised the event which is not entirely true. Keep in mind that the mediator is only an errrm…, well, a mediator. It mediates between independent objects. We need to see if there’s any listener by checking if ProductChanged is null.

Let’s add a component that will initiate the product selection change. You can think of this object as a combo box where the users can select products:

public class ProductChangeInitiator
{
	public ProductChangeInitiator(int selectedProductId)
	{
		SelectedProductId = selectedProductId;			
	}

	public int SelectedProductId { get; set; }

}

Add another class to the library called ProductChangeSimulation and a method called SimulateProductChange:

public class ProductChangeSimulation
{
        private List<Product> _allProducts = new List<Product>()
	{
		new Product(){Name = "FirstProduct", Id = 1, Registered = DateTime.Now.AddDays(-1), OnStock = 456}
		, new Product(){Name = "SecondProduct", Id = 2, Registered = DateTime.Now.AddDays(-2), OnStock = 123}
		, new Product(){Name = "ThirdProduct", Id = 3, Registered = DateTime.Now.AddDays(-3), OnStock = 987}
		, new Product(){Name = "FourthProduct", Id = 4, Registered = DateTime.Now.AddDays(-4), OnStock = 432}
		, new Product(){Name = "FifthProduct", Id = 5, Registered = DateTime.Now.AddDays(-5), OnStock = 745}
		, new Product(){Name = "SixthProduct", Id = 6, Registered = DateTime.Now.AddDays(-6), OnStock = 456}
	};

	public void SimulateProductChange(ProductChangeInitiator changeInitiator)
	{
		Product selectedProduct = (from p in _allProducts where p.Id == changeInitiator.SelectedProductId select p).FirstOrDefault();
		Mediator.Instance.OnProductChanged(changeInitiator, selectedProduct);
	}
}

We maintain a list of products in a private variable. We let the product change initiator to be passed into the method. We then call the OnProductChanged event handler of the mediator by passing the changeInitiator as the original sender and the selected product.

We now have the components ready in order to raise the event and pass in the necessary parameters. We’ll now need a listener. Add a new class called Warehouse:

public class Warehouse
{
	public Warehouse()
	{
		Mediator.Instance.ProductChanged += (s, e) => { SaveChangesInRepository(e.Product); };
	}

	private void SaveChangesInRepository(Product product)
	{
		Console.WriteLine("About to save the changes for product {0}", product.Name);
	}
}

We subscribe to the ProductChanged event of the mediator where the initiator of the event is unknown to Warehouse. It could be any component in reality: a customer, an e-commerce site, a physical shop, whatever. The Warehouse object won’t care. It only wants to know if there was a change so that it can go on with its job. As you see we declared the event subscription with a lambda.

Add a reference from the main project to the ComponentsMediator library. We can connect the pieces from Program.Main as follows:

ProductChangeInitiator initiator = new ProductChangeInitiator(2);
Warehouse warehouse = new Warehouse();
ProductChangeSimulation simulation = new ProductChangeSimulation();
simulation.SimulateProductChange(initiator);

We simulate the product id #2 was selected and start the simulation. You’ll see that Warehouse correctly displays the message in the console.

Let’s add another listener to the library project. Let’s say that the company CEO is a control freak and wants to be notified of all product changes:

public class CEO
{
	public CEO()
	{
		Mediator.Instance.ProductChanged += (s, e) => 
		{ 
			Console.WriteLine("This is the CEO speaking. Well done for selling {0}", e.Product.Name); 
		};
	}
}

Extend the code in Main as follows:

ProductChangeInitiator initiator = new ProductChangeInitiator(2);
Warehouse warehouse = new Warehouse();
CEO ceo = new CEO();
ProductChangeSimulation simulation = new ProductChangeSimulation();
simulation.SimulateProductChange(initiator);

You’ll see that the CEO is very happy now. We effortlessly added another listener to the product change. The CEO, Warehouse and ProductChangeInitiator objects don’t know about each other. This ensures that the components remain loosely coupled. The listeners won’t care where the notification is coming from, they are only connected to the mediator.

Asynchronous delegates

Let’s look at how asynchronous operations can be applied to delegates. Insert a new C# library to the project called AsyncDelegates. Insert a new class called AsynchronousProcessSimulation. Imagine that we start a long process and want to be notified of the progress made. Insert the following method that represents our lengthy process:

private void DoProgress(int maxValue)
{
	for (int i = 0; i <= maxValue; i++)
	{
		Console.WriteLine("Time : {0}", i);
		Thread.Sleep(50);
	}
}

We want to use a delegate to wire the information to the DoProgress method. We’ll do it with a private delegate:

private delegate void ShowProgressDelegate(int status);

Insert the following public entry point that initiates the delegate:

public void StartReporting()
{
	ShowProgressDelegate progressDelegate = new ShowProgressDelegate(DoProgress);
}

Let’s say that we want to invoke this delegate asynchronously. Delegates have a built-in method called BeginInvoke which comes in very handy. It will spin up a separate thread that will call into DoProgress. We can pass in the value for the maxValue parameter, a callback function and an additional object input which will be null in this example. We don’t need a callback either. Add the following async call to StartReporting:

progressDelegate.BeginInvoke(500, null, null);
Console.WriteLine("Finishing the StartReporting method.");

Insert a reference to this project from the main project. You can test the async delegate as follows:

AsynchronousProcessSimulation asyncSimulation = new AsynchronousProcessSimulation();
asyncSimulation.StartReporting();

You’ll see that “Finishing the StartReporting method.” is printed in the console after the delegate has been invoked on a different thread proving the point that BeginInvoke did in fact spawn up a new thread. The lengthy operation just keeps running in the background.

This post finishes this series on delegates and events. I hope you have learned some new stuff that you can use in your own project.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

3 Responses to Events, delegates and lambdas in .NET C# part 6: other scenarios

  1. Larisa says:

    Very usefull article, Andras!

  2. Atilla Selem says:

    very good..your efforts are appreciated -)

  3. Mac says:

    Excellent tutorial. I’ve been looking for a good example of implementing the mediator pattern using events and delegates. Thanks very much!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: