Freigeben über


There is something about Delegates

Whenever you might have started reading about Delegates, the first line you read is : "Delegates are function pointers in C#"

Now what is a function pointer? and how is it going to make my life any easier?

Lets understand function pointers now.

Function Pointers

In old C++ days, we used pointers. We do not use it anymore in our .Net managed world. Atleast all those people who use the luxary of Visual Studio don't.

A pointer is declared as:  

 1: int* p = &x;   
 2: // this actually allocates a block of memory of size 4 bytes
 3: // to store the address in memory of the variable x.   
 4: // I would like to mention that each memory allocation has an address attached to it.   
 5: // even this ponter thing has got an address in memory.    
 6: // So the following line of code is perfectly legal.   
 7: int** p2p = &p;
  
 The Function pointer is implemented as following in C++
    
 1: // lets create a simple function   
 2: int Add2Numbers(int i, int j)   
 3: {   
 4:     return i+j;   
 5: }   
 6: // So far so good, no flashy stuff!   
 7: // Now lets call this function   
 8: int x = Add2Numbers(2,5);   
 9: // So what, I have been using this since ages.  
 10: // Agreed, try this:  
 11: int (*p2F)(int, int) = &Add2Numbers;  
 12: int x = p2F(2, 5); // returns 7  
 13: // Oops, what was that?  
 14: // Here, p2F is a pointer to the function Add2Numbers.   
  
  
 So, whats the point?
  • You have a neater construct to call functions. You can call Add2Numbers, and Sub2Numbers by using other means like a "Switch", based upon user selection.
  • You are making calls by reference, it is considered faster than calling by value, also you make changes to the same data. You do not make copies, no redundancy issues. Works, doesn't it?

Delegates in C#

You will be glad to know that in our managed world, everything is managed ! Alright.... just trying to be funny.

An integral part of being managed is to be able to know in advance what you are going to do. It holds good for Delegates also.

So, unlike C++ where you can assign a pointer with whatever function you wish, we have "invented" Delegates. So, just like you declare an int, or a business object, you need to declare a Delegate. Our runtime now knows what you plan to do. No surprises at runtime please. If you offer surprises, be ready to be surprised by the runtime. You get in return at execution what you throw at it at design time. This is cool stuff, isn't it?

I am pretty sure, you now know the whereabouts of what I am talking right now. We use Delegates so that we can call functions/methods based on the requirements of the program in execution. It is defined for all methods/functions that match a given signature. But, you need to provide it with the function/method name. So, the complier still knows your intentions.

Let me share some features now:

  • They are object oriented, type safe, and secure.
  • An interesting and useful property of a delegate is that it does not know or care about the class of the object that it references. Any object will do; all that matters is that the method's argument types and return type match the delegate's.
  • A delegate declaration defines a type that encapsulates a method with a particular set of arguments and return type.

Getting bored? Don't worry, let my code do the talking...

 /// <summary>
     /// A separate class that contains delegates and functions
     /// </summary>
     public class MyDelClass
     {
         /// <summary>
         /// This delegate can be a pointer to all functions that doesnt have any return value and any args
         /// </summary>
         public delegate void NoReturn_And_NoArgDel();
  
         /// <summary>
         /// This delegate can be a pointer to all functions that has a return value but no args
         /// </summary>
         /// <returns></returns>
         public delegate string Return_But_NoArgDel();
  
         /// <summary>
         /// This delegate can be a pointer to all functions that has return value and args also
         /// </summary>
         /// <param name="arg"></param>
         /// <returns></returns>
         public delegate string Return_And_ArgDel(string arg);
  
         /// <summary>
         /// Constructor of the class
         /// </summary>
         public MyDelClass() { }
  
         /// <summary>
         /// A simple HelloWorld function that doesnt return a value or take any arg
         /// </summary>
         public void HelloWorld()
         {
             Console.WriteLine("Hello World");
         }
  
         /// <summary>
         /// A simple HelloWorld function that returns a value but takes no arg
         /// </summary>
         /// <returns></returns>
         public string HelloWorldReturns()
         {
             return "Hello World Returns";
         }
  
         /// <summary>
         /// A simple HelloWorld function that returns a value and takes an arg
         /// </summary>
         /// <returns></returns>
         public string HelloWorldReturns(string arg)
         {
             return arg;
         }
  
     }

Points to be noticed:

The signature:

 public delegate return_type delegate_name(arg_list)

ensures that the runtime knows in advance the type of your delegate. This ensures type safety.

Delegate is a sealed class. You can't inherit from it. So, its secure.

Now lets see what the entry point function has got for us:

 class Program
 {
     static void Main(string[] args)
     {
         //instantiate the class
         MyDelClass myDel = new MyDelClass();
  
         //First delegate
         MyDelClass.NoReturn_And_NoArgDel delNoReturnNoArg = new MyDelClass.NoReturn_And_NoArgDel(myDel.HelloWorld);
         delNoReturnNoArg.Invoke();
  
         MyDelClass.Return_And_ArgDel delReturnAndArg = new MyDelClass.Return_And_ArgDel(myDel.HelloWorldReturns);
         Console.WriteLine(delReturnAndArg.Invoke("Hello World from function Main()"));
  
         MyDelClass.Return_But_NoArgDel delReturnButNoArg = new MyDelClass.Return_But_NoArgDel(myDel.HelloWorldReturns);
         Console.WriteLine(delReturnButNoArg.Invoke());
  
         Console.Read();
     }
 }
  
 A delegate is an attribute of the class not of its instance.
 You need to use the Invoke() method of the instance of the delegate to call your methods encapsulated by the delegate synchronously.
  
 MyDelClass.NoReturn_And_NoArgDel delNoReturnNoArg = 
 new MyDelClass.NoReturn_And_NoArgDel(myDel.HelloWorld);
  
 Its possible for your delegates to call more than one function at a time:
  
 MyDelClass.NoReturn_And_NoArgDel delNoReturnNoArg = 
 new MyDelClass.NoReturn_And_NoArgDel(myDel.HelloWorld);
  
 delNoReturnNoArg += 
 new MyDelClass.NoReturn_And_NoArgDel(myDel.HelloWorld2);
  
 So, you can have more than one method hooked to a delegate.
  
 public delegate void d(string one); 
 d a = new d1(FuncOne);
 a.Invoke("1");
 d b = new d2(FuncOne);
 b.Invoke("2");
 d x = a + b;
 x.Invoke("3"); 
 // Result: 1233
 private void FuncOne(string one)
 {
     Console.Write(one);
 }
  

Comments

  • Anonymous
    April 23, 2007
    Simplifies it (I still think VB.NET makes delegates easier to understand). But there's a problem with your lines of text overflowing the RH margin (and not word wrapping). So some of your article is hidden.

  • Anonymous
    April 23, 2007
    Hate to mention it, but pointers do exist in C#, as pointers.  Of course, they're unsafe, but they do exist.

  • Anonymous
    April 23, 2007
    Hello Keith, I wonder if I mentioned otherwise anywhere, but pointers do exist in C#. All you need to do is to use the "unsafe" keyword to use it. Thanks for the comment.