Search This Blog

Tuesday, June 22, 2010

Delegates in .NET:


A delegate in C# is similar to a function pointer in C or C++. According to Microsoft A delegate is a type that references a method. Once a delegate is assigned a method, it behaves exactly like that method. The delegate method can be used like any other method, with parameters and a return value, as in this
 example:

public delegate int PerformCalculation(int x, int y);

Any method that matches the delegate's signature, which consists of the return type and parameters, can be assigned to the delegate. This makes is possible to programmatically change method calls, and also plug new code into existing classes. As long as you know the delegate's signature, you can assign your own delegated method.

This ability to refer to a method as a parameter makes delegates ideal for defining callback methods.
For example, a sort algorithm could be passed a reference to the method that compares two objects.
Delegate types are derived from the Delegate/System.Delegate class in the .NET Framework.

   Delegate types are sealed—they cannot be derived from— and it is not possible to derive custom classes from Delegate. Because the instantiated delegate is an object, it can be passed as a parameter, or assigned to a property. This allows a method to accept a delegate as a parameter, and call the delegate at some later time. This is known as an asynchronous callback, and is a common method of notifying a caller when a long process has completed. When a delegate is used in this fashion, the code using the delegate does not need any knowledge of the implementation of the method being used.

Delegates Overview
Delegates have the following properties:

1) Delegates are similar to C++ function pointers, but are type safe:
     Consider the following delegate declaration:
  
     Code:
          public delegate void MyDelegate (int myParam);

         This definition means that when one tries to pass a method to delegate instance, it MUST point to a  
         method that has the signature of one integer parameter, and no return  value.
             When using function pointers(c/c++), it is just a pointer, and I can cast it to anything, which could 
         lead to problems if I cast it to the wrong thing.

2) Delegates allow methods to be passed as parameters: 

Consider code bellow here we assigning a function which is to be passed as parameter to delegate then passing that delegate instance as an parameter to a method that invokes it.

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

namespace ConsoleApplication2
{
/*declaration of delegate*/
public delegate void methodTranspoter(string str);
class Program
{
static void Main(string[] args)
{
/*Instantiation of delegate*/
methodTranspoter inst = new methodTranspoter(method1);
CallTheMethod objct = new CallTheMethod();

/*
* passing function as parametre using delegate here,we passed delegate “inst” which ultimately point to a function
As parametre that get invoked
*/
objct.methodInvoker(inst, "parametreVal");
Console.ReadKey();

}
static void method1(string parm)
{
Console.WriteLine("I m In method1 and \"{0}\" is parametre",parm);
}
static void method2(string parm)
{
Console.WriteLine("I m In method2 and \"{0}\" is parametre", parm);
}
}
class CallTheMethod
{
/*Invoke Method passed as parametre*/
public void methodInvoker(methodTranspoter func,string parm)
{
func(parm);
}
}
}


3) Delegates can be used to define callback methods: 

The Callback function is normally implemented in the Business tier of a 3-tier architecture (of course there are exceptions to that). Once the Business Logic component implements a particular functionality, say a complex arithmetic calculation, it informs a client that the job was performed successfully. In order to inform the client, it needs an address of the function\method implemented in the client. The address of a function is just a memory address; this address does not carry any information such as number of parameters, data type of each parameter and its return value. Since the Callback function does not have any idea of the Method Signature it is going to call, in short, it is not Type Safe.

    However, Delegates provide the feature of Callback functions in a safe way. Taking the previous example, if the Business Logic finds that the function signature implemented by the Client differs in terms of number of parameters or parameter types or return values as opposed to the method signature, it is going to safely raise an error. 

For those familiar with design patterns, this is the observer design pattern.

Consider Following Code that illustrate the above concept:

Code:

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

namespace ConsoleApplication4
{
/*declaring delegate*/
public delegate void MakeDelegate (string PhoneNo);
class Program
{
static void Main(string[] args)
{
Customer objCustomer = new Customer();
/*instatiating delegate*/
MakeDelegate objDelegate = new MakeDelegate(NotifyClient);

/*reading user input*/
Console.WriteLine("Enter First Name:");
string FName = Console.ReadLine();

Console.WriteLine("Enter Last name:");
string LName = Console.ReadLine();

Console.WriteLine("Enter Phone Number:");
string PhoneNumber = Console.ReadLine();

objCustomer.FirstName =FName;
objCustomer.LastName = LName;

/*passing callback method as delegate instance
* if certain condition meet*/
objCustomer.ValidateCustomer(objDelegate, PhoneNumber);
Console.ReadKey();

}
/*method ultimately invoked on callback*/
private static void NotifyClient(string PhoneNo){
Console.WriteLine("This Customer is Eligible for 10% Discount");
}
}

class Customer{
public string FirstName;
public string LastName;
/*evaluate condition if required calls callback method */
public void ValidateCustomer (MakeDelegate objDelegate,String PhoneNo){
if(PhoneNo.StartsWith ("22")){
objDelegate.Invoke(PhoneNo);
}
/*if condition doesn't evaluate to be true no callback
* method called flow of execution doest get altered at all*/
}
}



}

4) Delegates can be chained together; 

For example, multiple methods can be called on a single event:

Consider following code sample DelegateSample is having 4 different notification methods of same signature.In Main() we created 4 delegateObj for each of the above method
We want all remaining 3 methods be chained to console deletegate instatance to do so we called

/*chaining is done here*/
_notifyConsole += _notifyEventLog;
_notifyConsole += _notifyDataBase;
_notifyConsole += _notifySound;

We changed our mind and removed sound device delegate instance from
Chain as follows

/*removing from chain*/
_notifyConsole -= _notifySound;

Now call to console notify function using corresponding delegate instance with

_notifyConsole("Hello");

This call lead to call to other 2 notify functions(db & eventlog) as they are chained but not sound notify function as it is not chained.

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

namespace ConsoleApplication5
{
//This is our delegate type
public delegate void Notify(string message);
class DelegateSample
{

public void NotifyToConsole(string _notificationMessage)
{
Console.WriteLine("Notifying Console " + _notificationMessage);
}

public void NotifyToEventLog(string _notificationMessage)
{
//Write an Entry in the Event Log
Console.WriteLine("Entry has been written in the event log as " + _notificationMessage);
}

public void NotifyDatabase(string _notificationMessage)
{
//Log in some database table
Console.WriteLine("Entry has been written to the DB as " + _notificationMessage);
}

public void NotifySound(string _notificationMessage)
{
//Log in some database table
Console.WriteLine("Entry has been written to the SOUND DEVICE as " + _notificationMessage);
}
}
class Program
{
static void Main(string[] args)
{
Notify _notifyConsole = new Notify(new DelegateSample().NotifyToConsole);
Notify _notifyEventLog = new Notify(new DelegateSample().NotifyToEventLog);
Notify _notifyDataBase = new Notify(new DelegateSample().NotifyDatabase);
Notify _notifySound = new Notify(new DelegateSample().NotifySound);

/*chaining is done here*/
_notifyConsole += _notifyEventLog;
_notifyConsole += _notifyDataBase;
_notifyConsole += _notifySound;

/*removing from chain*/
_notifyConsole -= _notifySound;

/*call to first one in chain will fire other two also*/
_notifyConsole("Hello");
Console.ReadKey();
}
}
}


5) Methods don't need to match the delegate signature exactly. 

For more information:Covariance and contravariance provide a degree of flexibility when matching method signatures with delegate types.

Covariance permits a method to have a more derived return type than what is defined in the delegate.
Contravariance permits a method with parameter types that are less derived than in the delegate type.

a) Covariance: 

This example bellow demonstrates how delegates can be used with methods that have return types that are derived from the return type in the delegate signature. The data type returned by SecondHandler is of type Dogs, which derives from the Mammals type that is defined in the delegate.

CODE: 

class Mammals
{
}

class Dogs : Mammals
{
}

class Program
{
// Define the delegate.
public delegate Mammals HandlerMethod();

public static Mammals FirstHandler()
{
return null;
}

public static Dogs SecondHandler()
{
return null;
}

static void Main()
{
HandlerMethod handler1 = FirstHandler;

// Covariance allows this delegate.
HandlerMethod handler2 = SecondHandler;
}
}

b) Contravariance :

This example bellow demonstrates how delegates can be used with methods that have parameters of a type that are base types of the delegate signature parameter type. With contravariance, you can now use one event handler in places where, previously, you would have to use separate handlers.

For example, you can now create an event handler that accepts an EventArgs input parameter and use it with the Button.MouseClick event that sends a MouseEventArgs type as a parameter, and also with TextBox.KeyDown event that sends a KeyEventArgs parameter.

CODE:

system.DateTime lastActivity;
public Form1()
{
InitializeComponent();

lastActivity = new System.DateTime();
this.textBox1.KeyDown += this.MultiHandler; //works with KeyEventArgs
this.button1.MouseClick += this.MultiHandler; //works with MouseEventArgs

}

// Event hander for any event with an EventArgs or
// derived class in the second parameter
private void MultiHandler(object sender, System.EventArgs e)
{
lastActivity = System.DateTime.Now;
}



6|) C# version 2.0 introduces the concept of Anonymous Methods, which permit code blocks to be passed as parameters in place of a separately defined method.:

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

namespace ConsoleApplication7
{
// Create a delegate instance
delegate void Del(int x);
class Program
{
static void Main(string[] args)
{
// Instantiate the delegate using an anonymous method
Del d = delegate(int k) 
{
   Console.WriteLine("{0}",k*k);
};
//Call to method defined anonymously above
d(2);

Console.ReadKey();
}
}
}


Delegates are of two types. 

1) Single-cast delegates /Uni-Cast Delegate
2) Multi-cast delegates 

A Single-cast delegate is one that can refer to a single method whereas a Multi-cast delegate can refer to and eventually fire off multiple methods that have the same signature.

A delegate is either public or internal.If no specifier is included in its signature then by default it is considered as internal

Uni-cast Delegate/ Single-cast Delegate:


Following Example illustrate Uni-cast Delegate in CSharp

using System;

namespace Delegates
{
public delegate int MyDelegate(int x, int y);

public class MyClass
{
public static int Add(int x, int y)
{
return x + y;

}

public static int Multiply(int x, int y)
{
return x * y;
}
}
class Program
{
static void Main(string[] args)
{
//create an instance of MyDelegate
MyDelegate delegate1 = new MyDelegate(MyClass.Add);

//Invoke Add method using the delegate;
int AddResult = delegate1(4, 6);
Console.WriteLine("4 + 6 = {0}\n", AddResult);

//create an instance of MyDelegate
MyDelegate delegate2 = new MyDelegate(MyClass.Multiply);

//Invoke Multiply method using the delegate;
int MultiplyResult = delegate2(4, 6);
Console.WriteLine("4 * 6 = {0}\n", MultiplyResult);

}
}
}

Multicast delegate:

 During Multicast Delegate's, a delegate object can maintain a list of methods to call, rather than a single method.
    If you want to add a method to the invocation list of a delegate object , you simply make use of the overloaded += operator, and if you want to remove a method from the invocation list you make use of the overloaded operator -=.

If you want to create a multicast delegate with return type you will get the return type of the last method in the invocation list. 

using System;

namespace Delegates
{
public delegate void MyMultiDelegate(int x, int y);

public class MyClass
{
public static void Add(int x, int y)
{
Console.WriteLine("4 + 6 = {0}\n", x + y);

}

public static void Multiply(int x, int y)
{
Console.WriteLine("4 * 6 = {0}\n", x * y );
}
}
class Program
{
static void Main(string[] args)
{
//create an instance of MyDelegate that points
// to the Add method.
MyMultiDelegate Multidelegate = new MyMultiDelegate(MyClass.Add);

//using the same instance of MulticastDelegate
//to call MyClass.Multibly() by adding it to it's
//invocation list.
Multidelegate += new MyMultiDelegate(MyClass.Multiply);

// This call both Add and Multiply methods
Multidelegate(4, 6);

//removing the Add() method from the invocation list
Multidelegate -= new MyMultiDelegate(MyClass.Add);

// This call onlly Multiply method
Multidelegate(4, 6);

}
}
}


Using delegate to call Static method:

class Program
{
public delegate void dels(string str);
static void Main(string[] args)
{
dels delsObj = new dels(printMsg);
delsObj("I m in simple delegate");
Console.ReadKey();
}

static void printMsg(string msg)
{
Console.WriteLine(msg);
}
}

Using Delegate to call non-Static Method:

Here delegate instance delObj is calling non-static method of Test Class by creating an object of Test class.

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

namespace ConsoleApplication6
{
public delegate void mydel(string str);
class Program
{
static void Main(string[] args)
{
Test TestObj = new Test();
mydel delObj = new mydel(TestObj.consoleWrite);
delObj("hi");
Console.ReadKey();
}
}
class Test
{
public void consoleWrite(string str){
Console.WriteLine(str);
}
}
}

No comments:

Post a Comment