Thursday, March 1, 2012

C# Delegates

The concept of delegates is somewhat confusing and developers often wonder why we need them and where would we use delegates? We will discuss delegates with an example and hopefully clarify potential uses for them.

Consider a list object of a specific type. You can encapsulate one or more objects of the same type in this list and pass the list to other methods to read and do something with those objects. Delegates are similar in nature except that they encapsulate references to the methods of an object.

A delegate can encapsulate any method as long as method signatures are same as those of delegates. Use can then pass around those delegates or use them just as you would use the method encapsulated in those delegates.

Let's consider an example.

In this example, we will create a class which will take the hourly pay, hours worked, tax rate and return weekly pay as well as tax.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApp1
{
    class CalculateWeeklyPay
    {
        private double _hourlypay;
        private double _hours;
        private double _overtimerate;
        private double _taxrate;

   public CalculateWeeklyPay(double hourlypay, double hours, 
                             double overtime, double taxrate)
        {
            _hourlypay = hourlypay;
            _hours = hours;
            _overtimerate = overtime;
            _taxrate = taxrate;
        }
        public double calculateTax()
        {
            double grosspay = calculateGrossPay();
            double tax = grosspay * ((double)_taxrate / 100);
            return tax;
        }
  public double calculatePay(double hourlypay, double hours, 
                             double overtime, double taxrate)
        {
            _hourlypay = hourlypay;
            _hours = hours;
            _overtimerate = overtime;
            _taxrate = taxrate; 
            return calculatepay();
        }
        public double calculatePay()
        {
            return calculatepay();
         }
        private double calculatepay()
        {
            double grosspay = calculateGrossPay();
            double netpay = grosspay-(grosspay * ((double)_taxrate / 100));
            return netpay;
        }
        private double calculateGrossPay()
        {
            double basepay;
            double overtimepay = 0;
            double grosspay;
            
            if (_hours > 40)
            {
                overtimepay = (_hours - 40) * (_hourlypay * _overtimerate);
                basepay = 40 * _hourlypay;
            }
            else
            {
                basepay = _hours * _hourlypay;
            }
            grosspay = basepay + overtimepay;
            return grosspay;
        }
     }
}

Now, lets create a console app, which will instantiate this object and then return the weekly pay and tax. We will create two delegates. One delegate will work with two methods (CalculatePay and CalculateTax) and another delegate will work with overloaded CalculatePay method.

Note, a delegate must have the same signature and same return type of the referenced method that it will encapsulate.

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

namespace ConsoleApp1
{
    class Program
    {
        //declared two deletages, one for each method
        //(since they have different signatures, you can't use same delegate)
        //also note return type for the delegate 
        //must be same as the return type of the signature.
        public delegate double delegate1();
        public delegate double delegate2(double hourlypay, double hours, 
                                         double overtime, double taxrate);
      
           
        static void Main(string[] args)
        {
            double hourlyPay = 7.50;
            double hours = 45;
            double overtimeRate = 1.5;
            double taxRate = 5;
          CalculateWeeklyPay calculatepay = new CalculateWeeklyPay(hourlyPay, 
                                                 hours, overtimeRate, taxRate);
            //you must instantiate a delegate
            delegate1 firstDelegate = new delegate1(calculatepay.calculatePay);
            Console.WriteLine("First Delegate {0} ", firstDelegate());
            Console.WriteLine(Environment.NewLine);
            //you can use same delegate for another method as long as 
            //its signatures and return type are same
            delegate1 anotherDelegate = new delegate1(calculatepay.calculateTax);
            Console.WriteLine("First Delegate Different Use {0} ", 
                              anotherDelegate());
            Console.WriteLine(Environment.NewLine);
            //let's use second delegate
            delegate2 secondDelegate = new delegate2(calculatepay.calculatePay);
            //you must pass the parameters that this delegate is expecting, 
            //otherwise a runtime error will occur
            Console.WriteLine("Second Delegate{0} ", 
                       secondDelegate(hourlyPay, hours, overtimeRate, taxRate));
            Console.WriteLine(Environment.NewLine);
            Console.Read();
        }
    }
}


One of the main use of delegates is in events. Suppose you want an object to notify another object when something happens. For example, a stock market object should fire an event when stock price changes for a stock you are watching. Another object (listener) can capture this event and do something with it instead of constantly polling the stock market object to see if the price has changed. In C#, you need delegates to create events such as this.

We will discuss events in future posts.

Thank you.

No comments:

Post a Comment