C# Delegates: Step by Step

September 12, 2010 § Leave a comment

C# Delegates – Step by Step

At first glance, delegates are difficult to absorb
because the usage of a delegate runs against conventional thinking on a
how a method should be called. During a conventional method call, you
create an object that contains the methods you want to call, invoke the
method, passing the required parameters. Everything happens in one pop,
including defining what object and method to call and invoking the call.
The method call through delegation takes a different approach. It
splits up the definition of the method call and the actual invocation of
the method into two parts. Delegates are the .NET version of addresses
to methods. They are type-safe classes that define the return types and
types of parameters. The delegate class not only not only contains a
reference to a method, but holds references to multiple methods. Even
though delegates have been around since .NET 2.0, they play an important
role in .NET 4 today. Lambda Expressions are directly related to
delegates. When the parameter is a delegate type, you can use a Lambda
expression to implement a method that’s referenced from the delegate.
Delegates exist for situations in which you pass methods around to other
methods. To see what this means, consider this code :

# Sample Snippet #

using System;
public class Test
 {
  public delegate int CalculationHandler(int x, int y);
  static void Main(string[]  args)
   {
     Math math = new Math();
    //create a new instance of the delegate class

     CalculationHandler sumHandler = new CalculationHandler(math.Sum);
    //invoke the delegate
      int result = sumHandler(8,9);
     Console.WriteLine("Result is: " + result);
     }
   }

 public class Math
  {
    public int Sum(int x, int y)
     {
       return x + y;
     }
  }

Result is: 17

The information encapsulated inside a delegate about a
method can be categorized into two groups: method signature and method
target. Method signature includes information about the number and type
of the method parameters as well as the return type. Method target
includes the name of the method and the object in which the method
resides. When we create a delegate object that encapsulates a method
call, we must provide these two sets of information.

The first thing we need to do to use delegation in
our program is to define a delegate class by specifying the signature of
the method the delegate class is capable of handling. In our example,
we defined a delegate class called CalculationHandler that is capable of
handling a method with two integer parameters and an integer return
value. If the method doesn’t have any return value, then we must use
“void” instead. After we have defined CalculationHandler, it becomes the
inner class of the existing class (in our case, the Test class).

After we have defined a delegate class, the next step
is to create an instance of the class and bind it to the specific
target. It is important to note that, first, the delegate class has only
one constructor, which takes the method target as its only parameter.
This binds the delegate object to a physical target. Second, the method
target specified in the constructor must match the method signature
defined in the delegate class. That is, we have to make sure that the
Sum method matches with the definition of CalculationHandler, which says
that the target method must take two integer parameters and have an
integer return value.

In this next example we instantiate a delegate of
type GetAString, and then initialize it so it refers to the ToString()
method of the integer variable x. Delegates in C# always syntactically
take one-parameter constructor, the parameter being the method to which
the delegate will refer. This method must match the signature with which
you originally defined the delegate. So in this case, we would get a
compilation error if we tried to initialize the variable
firstStringMethod with any method that did not take any parameters and
return a string. Notice that, because int.ToString() is an instance
method (as opposed to a static one), we need to specify the instance ( x
) as well as the name of the method to initialize the delegate
properly. The next line actually uses the delegate to display the
string. In any code, supplying the name of a delegate instance, followed
by brackets containing any parameters, has exactly the same effect as
calling the method wrapped by the delegate.

# Sample Snippet #

using System;
public class Program
 {
   public delegate string GetAString();
   public static void Main()
        {
            int x = 40;
            GetAString  firstStringMethod = x.ToString;
            Console.WriteLine("String is {0}", firstStringMethod());
            Currency balance = new Currency(34, 50);
             // firstStringMethod references an instance method
            firstStringMethod = balance.ToString;
            Console.WriteLine("String is {0}", firstStringMethod());
             // firstStringMethod references a static method
            firstStringMethod = new GetAString(Currency.GetCurrencyUnit);
            Console.WriteLine("String is {0}", firstStringMethod());

        }
    }

You see that there is a Currency class that creates an object:

# Sample Snippet #

using System;
public class Currency {
       public uint Dollars;
        public ushort Cents;
     public Currency(uint dollars, ushort cents)
        {
            this.Dollars = dollars;
            this.Cents = cents;
        }
    public override string ToString()
        {
            return string.Format("${0}.{1,-2:00}", Dollars, Cents);
        }
     public static string GetCurrencyUnit()
        {
            return "Dollar";
        }
    public static explicit operator Currency(float value)
        {
            checked
            {
                uint dollars = (uint)value;
                ushort cents = (ushort)((value - dollars) * 100);
                return new Currency(dollars, cents);
            }
        }
       public static implicit operator float(Currency value)
        {
            return value.Dollars + (value.Cents / 100.0f);
        }

        public static implicit operator Currency(uint value)
        {
            return new Currency(value, 0);
        }

        public static implicit operator uint(Currency value)
        {
            return value.Dollars;
        }
    }

csc.exe /t:library Currency.cs

csc.exe /r:Currency.dll Program.cs

OUTPUT:

String is 40
String is $34.50
String is Dollar

This example defines a MathOperations class that has a
couple of static methods to perform two operations on doubles. Then we
use delegates to call up these methods. The math class looks like this:

# Sample Snippet #

using System;
public class MathOperations
    {
        public static double MultiplyByTwo(double value)
        {
            return value * 2;
        }

        public static double Square(double value)
        {
            return value * value;
        }
    }

We call up these methods like this:

# Sample Snippet #

using System;
 delegate double DoubleOp(double x);

    class Application
    {
        static void Main()
        {
            DoubleOp[] operations =
            {
               MathOperations.MultiplyByTwo,
               MathOperations.Square
            };

            for (int i = 0; i < operations.Length; i++)
            {
                Console.WriteLine("Using operations[{0}]:", i);
                ProcessAndDisplayNumber(operations[i], 2.0);
                ProcessAndDisplayNumber(operations[i], 7.94);
                ProcessAndDisplayNumber(operations[i], 1.414);
                Console.WriteLine();
            }
        }

        static void ProcessAndDisplayNumber(DoubleOp action, double value)
        {
            double result = action(value);
            Console.WriteLine(
               "Value is {0}, result of operation is {1}", value, result);
        }
    }

Output:


Using operations[0]:
Value is 2, result of operation is 4
Value is 7.94, result of operation is 15.88
Value is 1.414, result of operation is 2.828

Using operations[1]:
Value is 2, result of operation is 4
Value is 7.94, result of operation is 63.0436
Value is 1.414, result of operation is 1.999396

In this code, we instantiate an array of DoubleOp
delegates (remember that after we have defined a delegate class, you can
basically instantiate instances just as we can with normal classes, so
putting some into an array is no problem). Each element of the array
gets initialized to refer to a different operation implemented by the
MathOperations class. Then, we loop through the array, applying each
operation to three different values. This illustrates one way of using
delegates — that you can group methods together into an array using
them, so that you can call several methods in a loop. The key lines in
this code are the ones in which you actually pass each delegate to the
ProcessAndDisplayNumber() method, for example:

# Sample Snippet #

ProcessAndDisplayNumber(operations[i], 2.0);

Here is an example that should clarify the meaning of
delegates. Notice how there are two categories of information for each
instance. As we have seen, this indirection between two objects
promotes “decoupling”. Putting a delegate, or method object between a
client and a server enables us to define certain actions without
invoking them. This could be advantageous when running multiple
applications to achieve a task. We would want each application to have
little knowledge of the other; therefore, if one application changes in
behavior, say the one providing a service, then the application
consuming the service will not alter its behavior. The consumer of the
service will not change its operating procedures if the service provider
suddenly changes its operating procedures. The example below, however,
is meant to stick with the general principals of delegates:

# Sample Snippet #

using System;
class Program
{
    /// Delegate declaration.
    delegate string UppercaseDelegate(string input);
      /// Delegate implementation 1.
    static string UppercaseFirst(string input)
    {
        char[] buffer = input.ToCharArray();
        buffer[0] = char.ToUpper(buffer[0]);
        return new string(buffer);
    }

    /// 
    /// Delegate implementation 2.
    /// 
    static string UppercaseLast(string input)
    {
        char[] buffer = input.ToCharArray();
        buffer[buffer.Length - 1] = char.ToUpper(buffer[buffer.Length - 1]);
        return new string(buffer);
    }

  
    /// Delegate implementation 3.
      static string UppercaseAll(string input)
    {
        return input.ToUpper();
    }

    /// 
    /// Receives delegate type.
    /// 
    static void WriteOutput(string input, UppercaseDelegate del)
    {
        Console.WriteLine("Your string before: {0}", input);
        Console.WriteLine("Your string after: {0}", del(input));
    }

    static void Main()
    {
        // Wrap the methods inside delegate instances and pass to the method.
        WriteOutput("gems", new UppercaseDelegate(UppercaseFirst));
        WriteOutput("gems", new UppercaseDelegate(UppercaseLast));
        WriteOutput("gems", new UppercaseDelegate(UppercaseAll));
    }
}

Yields

OUTPUT:

Your string before: gems
Your string after: Gems
Your string before: gems
Your string after: gemS
Your string before: gems
Your string after: GEMS

Review

To put delegates to use, you need to define one or
more handler methods, assign the handler when instantiating a delegate,
and perform an invocation on the delegate instance. Here is an
appropriate example:

# Sample Snippet #

using System;

// 1. Define delegate.
public delegate double UnitConversion(double from);

class QuickDelegateDemo
{
    // 2. Define handler method.
    public static double FeetToInches(double feet)
    {
        return feet * 12;
    }

    static void Main()
    {
        // 3. Create delegate instance.
        UnitConversion doConversion = new UnitConversion(FeetToInches);

        Console.WriteLine("Feet to Inches Converter");
        Console.WriteLine("------------------------\n");
        
        Console.Write("Please enter feet:  ");
        double feet = Double.Parse(Console.ReadLine());

        // 4. Use delegate just like a method.
        double inches = doConversion(feet);

        Console.WriteLine("\n{0} Feet = {1} Inches.\n", feet, inches);
        Console.ReadLine();
    }
}

OUTPUT:

Feet to Inches Converter
------------------------

Please enter feet:  12

12 Feet = 144 Inches.

When we use a delegate to encapsulate method calls,
our mindset must shift from the conventional view of method calls to an
object-oriented view. Asynchronous programming produces powerful
applications for delegates. Similar to waiting on a long, drawn out
shopping line and being, idle, perhpas an analogy would be performing a
task while waiting for the person in front of you.

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 )

Google+ photo

You are commenting using your Google+ 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

What’s this?

You are currently reading C# Delegates: Step by Step at Naik Vinay.

meta

%d bloggers like this: