Delegates in C#

Another very interesting feature in C# is delegates. Delegates are best complemented as new type of Object in C#. They are also represented as pointer to functions. Technically Delegates are reference types which allow indirect calls to methods.
 
A delegate instance holds references to some number of methods, and by invoking the delegate one causes all of these methods to be called. The usefulness of delegates lies in the fact that the functions which invoke them are blind to the underlying methods they thereby cause to run (see, for instance, the discussion of events, below).
 
From this brief description, it can be seen that delegates are functionally rather similar to ‘function pointers’ in C++. . However, it is important to bear in mind two main differences. Firstly, delegates are reference types rather than value types. Secondly, some single delegates can reference multiple methods.
 
Creating a Delegate
 
The general syntax of the delegate is:
 
<Modifier> delegate <return type> delegatename (parameters);
 
A delegate is created with the delegate keyword, followed by a return type delegate name and the signature of the methods that can be delegated to it, as in the following:
 
public delegate int MyFirstDelegate(object obj1, object obj2); //Return //Type integer public delegate void SimpleDelegate(); //Return Type void or No Return //Type
 
This declaration defines a delegate named MyFirstDelegate, which will encapsulate any method that takes two objects as parameters and that returns an int.
 
There are three steps in defining and using delegates:
 
• Declaration
• Instantiation
• Invocation
 
using System;
namespace ebiz.BasicDelegate
{
    // Declaration
    public delegate void SimpleDelegate();

    class TestDelegate
    {
        public static void MyFunc()
        {
            Console.WriteLine("I was called by delegate ...");
        }

        public static void Main()
        {
            // Instantiation
            SimpleDelegate simpleDelegate = new SimpleDelegate(MyFunc);

            // Invocation
            simpleDelegate();
        }
    }
} 
 
Once the delegate is defined, you can encapsulate a member method with that delegate by instantiating the delegate, passing in a method that matches the return type and signature. As an alternative, you can use anonymous methods as described later. In either case, the delegate can then be used to invoke that encapsulated method.
 
Types of Delegates
 
There are basically two types of delegates:
 
? Single Cast delegate
? Multi Cast delegate
 
Let us first understand at the broader level the definition of both types of delegates. A single cast delegate can call only one function. A multi cast delegate is one that can be part of a linked list. The multi cast delegate points to the head of such a linked list. This means that when the multi cast delegate is invoked, it can call all the functions that work as a part of the linked list.
 
Suppose that I have that i have several clients who would like to receive notification when a particular event occurs. Putting all of them in a multi cast delegate can help call all the clients when a particular event occurs.
 
To support a single cast delegate the base class library includes a special class type called System.Delegate. To support multi cast delegates the base class library includes a special class type called System.MultiCastDelegate..
 
Single-Cast Delegates
 
A single-cast delegate is called a single delegate that derives from the System.Delegate class contains an invocation list with one method. Now we will look at how the single-cast delegates are declared. In single-cast delegates, the delegate can point to both static and non-static method if both have the same signature as the delegate. The signature of a single cast delegate is shown below.
 
public delegate Boolean DelegateName (parm1, parm2);
 
When the compiler compiles the statement above, it internally generates a new class type. This class is called DelegateName and derives from System.Delegate. Just to make sure just check the ILDisassembler code to check that this is happening. As an example let us create a single cast delegate named MyDelegate which would point to a function MyFunction. The code appears as below,
 
Example
 
//Create Single Cast Delegate
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;
namespace ConsoleApplication4
{
    public delegate Boolean MyDelegate(Object sendingobj, Int32 x);

    public class TestDelegateClass
    {
       public Boolean MyFunction(Object sendingobj, Int32 x)
        {
            Console.WriteLine("Delegate Program");
            //Perform some processing           
            return (true);
        }

        public static void Main(String[] args)
        {
            //Construct an instance of this class
            TestDelegateClass tdc = new TestDelegateClass();

            //Instantiate the delegate passing the method to invoke in its //constructor
            MyDelegate mdg = new MyDelegate(tdc.MyFunction);

            //The following line will call MyFunction
            Boolean f = mdg(tdc, 1);
            Console.ReadLine();
        }
    }
}

 
In the above example, the System.Delegate contains a few fields. The most important of them are Target and Method. The Target field identifies an object context. In the above scenario this would point to the TestDelegateClass being created. If the method to be invoked is a static method then this field will be null. The Method field identifies the method to be called.
 
This field always has some value. It is never null. To the intelligent reader, if you observe the ILDisassembler code you would see that the constructor for MyDelegate contains two parameters. But in our case we are passing only one parameter? Any guesses on how this is done? Yeah you are right! It gets done internally by the compiler. The compiler resolves the call above to a call passing in the two parameters required.
 
If you observe in C++ with managed extension, the object and the address of the function have to be passed explicitly. But in VB and C# we have done away with this with the compiler filling in the necessary details. In the code sample above we saw,
 
Boolean f = mdg (this, 1);
 
When the compiler comes across this line, then the compiler compiler identifies that mdg is a delegate object. The delegate object has the target and method fields as described above. The compiler generates code that calls the Invoke method of the System.Delegate derived class i.e MyDelegate.
 
The Invoke method internally uses the MethodInfo type identified by the delegate’s Method field and calls the MethodInfo’s invoke method. The MethodInfo’s invoke method is passed the delegates Target field which identifies the method and an array of variants which contains the parameters to the method to be called.
 
In our case, the array of variants would be an Object and an Int32 value. The above discussion would have made you clear about what happens internally when a method gets called through a Single Cast delegate.
 
Multi-Cast Delegate
 
A delegate is called Multi-cast Delegate that derives from the System.MulticastDelegate that contains an invocation list with multiple methods. At times it is desirable to call two methods through a single delegate. This can be achieved through Single-cast Delegate but it is different from having a collection, each of which invokes a single method.
 
In Multi-casting you create a single delegate that will invoke multiple encapsulated methods. In multicast delegate, the return type of all the delegates should be same. Now the question why are we using Multi-cast delegates when Single-cast delegates are enough? Answer of this question is what if you want to call three or more methods when a button is clicked.
 
The Multi-cast delegates are used with events where multiple calls to different methods are required. System.MulticastDelegate contains two methods Combine and Remove. The Combine is a static method of class System.MulticastDelegate and is used to combine the delegates and the remove method is used to remove the delegate from the list.
 
For user convenience we have += operator overloaded for delegate Combine method and -= operator overloaded for Remove method Multi-cast delegates are similar to Single-cast delegates for e.g.
 
public delegate void MulticastDelegate();
 
Multi-cast delegate can have arguments and can return value as well. All the Methods pointed by delegates should return the similar value as the delegate return type.
 
public delegate string MultiCastOne(string Name);
 
Example:
 
using System;
namespace Multi_castDelegate
{
  /// 

/// Summary description for Class1 ///

  class MyClassDelegate
  {
    /// 

///The main entry point for the application ///


    public delegate string StringDelegate(string s);
  }
}
 
Below is the class that defines the static methods having same signature as delegate.
 
using System;
namespace Multi_castDelegate
{
  /// 

/// Summary description for MyImplementingClass ///

   public class MyClass
   {
      public MyClass()
      {
                  
      }

      public static string WriteString(string s)
      {
        Console.WriteLine("Writing string");
        return "null";
      }

      public static string logString(string s)
      {
        Console.WriteLine ("loging string");  
        return "null";
      }

      public static string TransmitString(string s)
      {
        Console.WriteLine ("Transmitting string");  
        return "null";
      }
   }
}
 
The Main class:
 
using System;
using System.Threading; 
namespace Multi_castDelegate
{
  /// <summary>
  /// Summary description for Test
  /// 
  public class Test
  {
    public static void Main()
    {
      MyClassDelegate.StringDelegate Writer, Logger, Transmitter;
      MyClassDelegate.StringDelegate myDelegate;
 
      Writer=new 
             MyClassDelegate.StringDelegate(MyClass.WriteString);
 
      /// calling Writer
      Writer("hello i am Writer just acting like Single cast");
 
      Logger=new 
             MyClassDelegate.StringDelegate (MyClass.logString);
      
      ///calling Logger
      Logger ("hello i am Logger just acting like Single-cast");
      
      Transmitter=new              
        MyClassDelegate.StringDelegate (MyClass.TransmitString);
      
      ///calling Transmitter
      Transmitter ("hello i am Transmitter just acting like Single-cast");  
      
      ///here mydelegate used the Combine method of 
///System.MulticastDelegate and the delegates combine   
      myDelegate =(MyClassDelegate.StringDelegate)
      System.Delegate.Combine (Writer, Logger);  
      myDelegate("used Combine");

      ///here Transmitter is also added using the overloaded form of ///Combine
      myDelegate +=Transmitter;
      myDelegate ("Using Overloaded Form");
 
      ///now using the Remove method
      myDelegate =(MyClassDelegate.StringDelegate)
      System.Delegate.Remove (myDelegate,Writer);
      myDelegate("Without Writer");     

      ///overloaded Remove
      myDelegate -=Transmitter;
      myDelegate ("Without Transmitter");
      System.Threading.Thread.Sleep(2300);       
    }
  }
}

 
The above program contains three classes, MyClass Delegate contains the delegate.
 
public delegate string StringDelegate(string s);
 
The class MyClass contains the static methods WriteString(), logString() and TransmitString() that have a similar signature as the delegate StringDelegate. The third class is the Test Class which shows how to combine the delegates and how to remove the delegate from the list.
 
Scroll to Top