Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using Delegates to Address Common Programming Issues

0.00/5 (No votes)
25 Aug 2015 1  
The article shows how we can solve various technical problems easily with the help of delegates.

Introduction

In our day-to-day programming with the latest .Net frameworks (3.5 & above) we use delegates extensively in one or the other way. It might be in the form of events or with LINQ or with Task/Threads or with MVC Rzaor helpers, the list goes on. Since the introduction of lambda expression & LINQ the delegate became so natural to us.

Most of us use delegates by providing anonymous method implementation for the framework methods, but when it comes to our own business logic or some code the delegate just doesn't come in our mind. Many people use delegates just for events, but by observing the base class libraries we can easily make out that how useful the delegates are! This article shows what are the different problems that we can solve using delegates and how they make our life simpler. So, next time when you are ready to solve some problem, give a thought for delegate as well :)

Background

I assume that you already knew what delegates and lambda expressions are, how they are related, what are the different in-built delegates provided by the framework and how to use them. If not, here is a a good link to learn. Here is another link which talks about anonymous function and events as well.

Using the code

1. Avoid class library dependency using delegates

Lets say that you have 3 class library projects namely Repositoy, Common and Service. As the name suggests Repository is intended to hold various repositories which contains data access logic. Similarly Service is a project which is intended to hold business logic, and the Common project is used to hold some common logic across your solution.

Assume that, there is a requirement in which you need a common logic which depends on one of the Repositoy, but you don't want your Common assembly to have a dependency on that. In the situations as such, people generally solve it either by creating a helper service or by introducing one more abstraction, in the Service assembly itself. But this solution has an arguable downside, that is - any other assembly that needs this logic has to use the service abstraction, which is not required otherwise. And this is arguable because, the moment you have a dependency on Repo, most probably the logic is best suited for the Service assembly than the Common assembly and the solution is following the proper design principles/guidelines. The other side of the argument is reusability which our solution lacks here. Sometimes the real life business requirement is so complex that it contradicts with the design principles. The argument goes on but keeping these things apart (as it is out of the scope of this article), I will concentrate on the delegate implementation part.

If you are confused with all these, don't worry! you will understand once you look into the code sample. Here is the code snippet which I am talking about:

The Repository Assembly -

    // A class in Repo Assembly
    class Repo
    {
        public int GetValue()
        {
            return 100;
        }
    }

The  Common Assembly - 

    // A class in Common Assembly
    class Common
    {
        // TODO: Avoid the dependecy on Repo
        static Repo repo = new Repo();

        public static int OperateOnRepoValue()
        {
            // Operates on the value returned by the repository
            return repo.GetValue() * 2;
        }
    }

The Service Assembly - 

    // A class in Service Assembly
    class Service
    {
        // Already have a dependency on Repo and we are happy with it
        Repo repo = new Repo();

        public void Operate()
        {
            int result = Common.OperateOnRepoValue();
        }
    }

As you can see in the above code, the Common class is using GetValue method of Repo creating dependency on Repository assembly. As discussed earlier, one way of solving this problem is by introducing an abstraction on the service layer or by introducing a new service just for the purpose of operating on the Repository value in a re-usable manner. However, I will not give the code sample for this as it is out of the scope of this article; instead, I will show how to solve this using delegate.

Solution using a Function delegate

    // A class in Common Assembly
    class Common
    {
        // Takes a function delegate, which returns an integer
        public static int OperateOnRepoValue(Func<int> func)
        {
            return func() * 2; // Multiply 2 into the return value of func and return the result
        }
    }

The above code removes dependency on the Repository by accepting  a function delegate as parameter. Now, the caller can provide implementation for that delegate in the way it wish.

Here is our updated Service class -

    // A class in Service Assembly
    class Service
    {
        Repo repo = new Repo();

        public void Operate()
        {
            // Pass an anonymous function which returns a value from the Repo
            int result = Common.OperateOnRepoValue(() => repo.GetValue());
        }
    }

2. Measuring various function/method execution using StopWatch (An Using pattern with delegate)

At times, we need to measure the elapsed time of a function/method, and it is recommended that you always use StopWatch for maximum accuracy. In case when we have to measure multiple functions, we always end-up with multiple StopWatch wrappers around our function under test. Here is an example:

Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();

myMethod_1();

stopWatch.Stop();
Console.WriteLine(string.Format("{0}-Elapsed: {1}", "My Method 1", stopWatch.Elapsed));

stopWatch.Restart();

myMethod_2();

stopWatch.Stop();
Console.WriteLine(string.Format("{0}-Elapsed: {1}", "My Method 2", stopWatch.Elapsed));

We can avoid this cluttering code and always reuse the measuring logic this way - 

public static void Measure(Action action, string actionName)
{
    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();
    action();
    stopWatch.Stop();
    Console.WriteLine(string.Format("{0}-Elapsed: {1}", actionName, stopWatch.Elapsed));
}

The above method takes an Action delegate and wraps it under the StopWatch. You can use it like this:

        static void Main(string[] args)
        {
            Measure(() => MyMethod_1(), "My method 1");

            // Similarly, measure other methods

            Console.ReadKey();
        }

        public static void MyMethod_1()
        {
            // Sleep for 2 seconds
            Thread.Sleep(1000 * 2);
        }

In case, if your function under test is having a return value, use the generic function delegate instead. Here is the code sample - 

public static T Measure<T>(Func<T> func, string actionName)
{
    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();
    T result = func();
    stopWatch.Stop();
    Console.WriteLine(string.Format("{0}-Elapsed: {1}", actionName, stopWatch.Elapsed));
    return result;
}

Now in the main method - 

var result = Measure(() => MyMethod_1(), "My method 1");

3. Factory pattern using delegate (not factory method)

Sounds crazy?!.....not really, it looks natural once you have it in place.

Note: When I say factory, I am talking about a simple object creation, don't confuse it with Factory method pattern which involves inheritance.

Lets say that you have an Asp.Net MVC application which has an action method in a controller which accepts a parameter. Based on this parameter you may need to instantiate different Services. You might be using a dependency container to resolve the service (or any) dependencies. If the dependency is known at compile time, then we are good and can easily register the dependencies at application startup/config file. But in our case the resolution should happen at runtime based on the provided parameter, but you may not want to scatter the dependency resolution all over the place. You can easily solve this by using a delegate. Here is the steps - 

Create a class similar to this:

    public static class ServiceFactory
    {
        // The Create property returns a generic function delegate, which accepts MyOptions enum
        // and returns an instance of IService implementation.
        public static Func<MyOptions, IService> Create { get; set; }
    }

Where, MyOptions is an enum

enum MyOptions
{
    Option1,
    Option2,
    Option3
}

Now in your application startup, on the bootstrap/component registration method you can resolve the dependencies like this:

ServiceFactory.Create = (myOptions) =>
    {
        // Resolve dependencies based on the options
        switch (myOptions)
        {
             case MyOptions.Option1: return container.Resolve<Service1>();
             case MyOptions.Option2: return container.Resolve<Service2>();
             case MyOptions.Option3: return container.Resolve<Service3>();
             default: return container.Resolve<Service1>();
        }
    };

So, in the action method you just need to rely on the factory to resolve the dependency:

public ActionResult Index(string id, MyOptions myOptions)
{
    IService service = ServiceFactory.Create(myOptions);
}

4. Changing behavior of a property/method at runtime (An Using pattern with delegate)

Lets say that you are using entity framework for data access and you created generic repository to deal with the entities. Now for some reason (usually performance) you want to schedule multiple repo calls simultaneously using parallel execution capabilities of .Net framework. Unfortunately, the generic repository pattern will not allow you to do this out-of-the-box as the DB Context allows execution of only one thread at a time. In this case, the obvious solution is to create each repo instance in its own new DB Context.

In your service class you might have lot of methods and we are un-sure that at what time which methods will execute in parallel. Also, the methods which participates in parallel execution may change over time based on the business requirement and performance measures. In the scenario's like this you can always take help of delegates and solve it in a more maintainable manner. Here is the code sample -

    public abstract class BaseService<Entity> where Entity: class
    {
        // Flag to switch the mode
        private bool isConcurrencyEnabled = false;
        // The repository instance to re-use
        private IRepository<Entity> _repo = null;

        protected IRepository<Entity> Repo
        {
            get
            {
                if (this.isConcurrencyEnabled)
                {
                    // Return new instance of the repository
                    return RepoFactory.GetRepo<Entity>();
                }
                else
                {
                    // Re-use existing repository
                    return this._repo;
                }
            }
        }

        public BaseService(IDbContext dbContext)
        {
            // Initialize repository
            this._repo = dbContext.GetRepository<Entity>();
        }

        protected T ExecuteInParallel<T>(Func<T> action)
        {
            // Use try/finally pattern to leave the class always in a proper state
            try
            {
                // Enable the flag before executing the action
                this.isConcurrencyEnabled = true;
                // Execute the action and return the result
                return action();
            }
            finally
            {
                // Always disable the flag, before leaving the function
                this.isConcurrencyEnabled = false;
            }
        }
    }

The concrete service class can use it like this - 

    public class MyService: BaseService<MyData>
    {
        public MyService(IDbContext dbContext)
            : base(dbContext)
        { }

        public List<MyData> GetMyData(List<int> ids)
        {
            if(ids == null)
            {
                throw new ArgumentNullException("The ids cannot be null");
            }

            // We need to restrict number of elements to balance the SQL connection pool
            if(ids.Count > 5)
            {
                throw new ArgumentException("The number of elements should not exceed 5");
            }

            return this.ExecuteInParallel(() =>
            {
                ConcurrentBag<MyData> myData = new ConcurrentBag<MyData>();

                // The context used in Repo of GetMyDataById will be different for each id
                ids.AsParallel().ForAll(id => myData.Add(this.GetMyDataById(id)));

                return myData.ToList();
            });
        }

        public MyData GetMyDataById(int id)
        {
            return this.Repo.Find(x => x.Id == id).FirstOrDefault();
        }
    }

The consumer of MyService can call GetMyDataById multiple times with various ids one by one (which re-uses the context) or can make use of GetMyData by providing a list of ids which executes parallelly (creates new context for each repo call). This particular example does not make much sense but this is just to give an idea of how we can achieve Using pattern with delegate. In reality it can be implemented in a greater extent and it is very powerful! possibly I will write a dedicated article on this topic.

Note: The exception is not handled in the above code for brevity but in real projects you must handle it.

Points of Interest

Here we have seen how we can solve various problems using delegates. At the same time, it warns you that these are not some of the out-of-box solution. The extensive usage of delegates makes your code more difficult to debug and understand. You need to apply them thoughtfully. The key thing to notice here is that, we are applying delegate to those part of the code which varies. If you keep this in mind, you will simply know where and when to apply it.

History

August 24, 2015 - Initial Version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here