Sunday 1 September 2013

C# Delegates Series Part 2 - Delegates allows methods to be passed as parameters

Below is the reference of all posts in this series

  1. C# Delegates Series Part 1 - Overview
  2. C# Delegates Series Part 2 - Delegates allows methods to be passed as parameters
  3. C# Delegates Series Part 3 - Delegates can be used to define event handlers and event handling mechanism
  4. C# Delegates Series Part 4 - Delegates can be used to Invoke methods asynchronously

In part 1, we understood brief overview of delegates and how to create/use delegates. In this post we will look at how delegates can be used to pass methods as parameters and how would this be useful in real scenario.

We are used to passing properties as parameters to methods in same or different class/forms. But what about passing methods as parameters to other method/class. Is that really useful? If yes, how can we do that? Answer lies below.

Suppose you are asked to develop a component that loads all objects (ex assets) from database and display to user for selection. This component will be reused and called from multiple application.

Now you are asked to design the component to be flexible such that business/filter logic to decide weather an object (asset) to display or not should reside in consuming application (and not in the component) and can vary from application to application.

Ex: One client application can define filter criteria to load only those items (assets) that are of Category "X". Where as other client application can define filter criteria to load only those items (assets) that belong to Location "X". Consuming application should be able to define its own filter criteria without any change required in the component.

Design for creating such component would require that component would fetch all assets from database, and then for each item call some function in consuming application that has filtering logic to check if asset needs to be displayed or not and returns Boolean value. This is one such scenario where delegates can be extremely useful.

Now if you think of methods like FindAll, Select on generic List in C#, this would start making some sense. Internally these methods use delegates.

Let’s take an example of how you would achieve such functionality using delegate. To keep this language neutral, we take 3 simple class and use simple pseudo code. Download the attached sample for complete example in plain C#, Windows forms and WPF.

AssetItem.cs

public class AssetItem
{
public string ID { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string Category { get; set; }
}

AssetSelector.cs (Component part)

public class AssetSelector
{
// Declaring delegate
public delegate bool FilterDelegate(AssetItem item);

public FilterDelegate FilterMethod;

public AssetSelector(FilterDelegate filterMethod)
{
// Create an instance of delegate and assign client side method passed as parameter.
FilterMethod = new FilterDelegate(filterMethod);
}

public void LoadAssets()
{
// Load all assets from database.
List<AssetItem> Assets = GetAssets();

List<AssetItem> filteredAssets = new List<AssetItem>();
foreach (AssetItem item in Assets)
{
// Using delegate to invoke client side filter method passed as reference.
if (FilterMethod(item))
filteredAssets.Add(item);

}

// Bind data grid with filteredAssets;
}

private static List<AssetItem> GetAssets()
{
List<AssetItem> Assets = new List<AssetItem>();
return Assets;
}
}

Client.cs (Consuming application part)

public class Client
{
public void SelectAssets()
{
AssetSelector selector = new AssetSelector(FilterMethod);

// Pass "FilterMethod" method as parameter.
selector.LoadAssets();
}

// Declare method that matches delegate signature and contains filtering logic.
public bool FilterMethod(AssetItem item)
{
if (item.Category == "Electronics")
return true;
else
return false;
}
}

 


Implementing callbacks with delegates


If you understand the above concept of using delegates, then implementing callbacks would be similar and easy task for you. Steps remains the same.



Parent/Client class would create a CallbackMethod and pass this method as an argument when calling the component/another class.


Component class would create the delegate with same method signature, store the reference of CallbackMethod in delegate and task is completed use the delegate to invoke CallbackMethod.


Callback makes more sense when you are designing a component that performs a some task asynchronously or uses API/third party service that provides response asynchronously. In this case client application would call the component to perform the task and continue with other operations instead of waiting for the component to return the result. This makes client application responsive. Once component has completed the task, component use the delegate to provide callback to the client application.

No comments:

Post a Comment