Deep C# - Delegates
Written by Mike James   
Wednesday, 06 January 2016
Article Index
Deep C# - Delegates
Signatures and Methods

Delegates are C#'s original way of allowing you to work with functions as if they were first class objects. The aim may be simple but the need to define a type and then an instance of the type can be confusing. Let's see if we can make it all seem logical.

This is a chapter of our ebook on C# - a work in progress.

 

Deep C#

 Buy Now From Amazon

DeepCsharp360

 Chapter List

  1. Why C#?

    I Strong Typing & Type Safety
  2. Strong Typing
       Extract 
    Why Strong Typing
  3. Value & Reference
  4.    Extract Value And Reference
  5. Structs & Classes
       Extract
    Structs & Classes 
  6. Inheritance
      
    Extract
    Inheritance
  7. Interfaces & Multiple Inheritance
      
    Extract Interface
  8. Controlling Inheritance
    II Casting & Generics
  9. Casting - The Escape From Strong Typing
      
    Extract Casting I ***NEW!
  10. Generics
  11. Advanced Generics
  12. Anonymous & Dynamic
    Typing
    III Functions
  13. Delegates
  14. Multicast Delegates
  15. Anonymous Methods, Lambdas & Closures
    IV Async
  16. Threading, Tasks & Locking
  17. The Invoke Pattern
  18. Async Await
  19. The Parallel For
    V Data - LINQ, XML & Regular Expressions
  20. The LINQ Principle
  21. XML
  22. LINQ To XML
  23. Regular Expressions
    VI Unsafe & Interop
  24. Interop
  25. COM
  26. Custom Attributes
  27. Bit Manipulation
  28. Advanced Structs
  29. Pointers 

Extra Material

 <ASIN:1871962714>

 <ASIN:B09FTLPTP9>

 

Delegates are at the core of a number of different .NET facilities, events in particular.

It’s long been a truism that the way to get work done is to delegate, but what are C# delegates all about?

First we have to understand what the problem is that delegates are designed to solve.

First Class Functions

In many languages functions are treated in the same way as objects. In JavaScript for example function are objects - this is often expressed by saying the functions are first class objects or they are first class functions. 

Why should this matter?

The simple answer is that sometimes you want to pass a function as an argument to a method call. There are other things you might want to do with a first class function including having a reference to it or having an array of such functions but if we concentrate on the most common usage - passing functions to methods - then we have the key idea. 

For example suppose you have a sorting routine you might want to pass in a function that determines what A>B means, i.e. return true if A is greater than B and false otherwise, where A and B are objects of some type. 

This seems like a reasonable thing to do but notice that in C# and in other object oriented language there are no "disembodied" functions. All functions are methods that belong to some class or instance of a class. This make things a little more complicated because now you cannot simply create a function called myOrder and pass it into a method because myOrder has to be a method of some class or object.

You might think of defining something like:

class Compare{
 public myOrder(){};
}

and now you could try to pass and instance of the object:

Compare myCompare=new Compare();
myData.Sort(myCompare.myOrder);

That is you are trying to pass myCompare.myOrder to the Sort method. 

This is one approach and it could be made to work but C# is strongly typed and every parameter in a method has to have a type.

What type is Compare.myOrder?

Its a method of a type and not a type in its own right and trying to make it a type will quickly become very messy. 

A different alternative is to demand that every time you want to pass a function you have to pass an object that has that function as a method. This is what Java does for example. In this case we don't have a problem with types:

Compare myCompare=new Compare();
myData.Sort(myCompare);

However within the Sort method the myOrder function has to be called as 

myCompare.myOrder();

In other words to pass a function you have to pass an entire object which wraps the function. Of course the one advantage of this method is that in principle you could pass a whole set of functions in one go but this doesn't make up for the inconvenience of the approach when you are trying to pass just one function. 

As any Java programmer will tell you this approach is workable but very verbose. You have to define a class for every function you want to pass and create and instance to pass every time you want to pass it. It is the original method that Java used to implement event handling and over time new facilities such as anonymous classes and eventually lambdas have been added to the language to make this cumbersome method easier to use. 

The C# Approach

The overall C# approach to allowing functions to be first class entities has developed over time as well. Initially delegates were used as a way of wrapping a function in an object in a way that is superficially similar to Java's approach but it also has some special advantages. Over time delegates acquired features that made them easier to use anonymous methods and finally lambdas. 

However it is important that you fully understand the delegate idea and how to use them.

Let’s look at how it works and some of the more interesting ways that you can put it to work.

Delegates and their relationship to events is covered in another chapter.

Delegate basics

What is initially confusing is that to create a delegate you first have to create a type and then create an instance of the type. That is, delegate is a user-defined reference type that encapsulates a method.

Consider, for example, how to encapsulate the simple example method:

public int hello(int param1){
 MessageBox.Show(
  "Hello delegate World"
       +param1.ToString());
 return(++param1);
}

All this does is to display the current value of param1 and then return and incremented value.

First we need to define a delegate type that matches its signature – including, in this case, the return type:

delegate int HelloType(int param);

This delegate type defines the methods that it can encapsulate. Notice that it is only the functions signature and return type that specifies the delegate type. The HelloType delegate can wrap any function that has the specified signature and return. 

Next we have to create an instance of the type and supply it with the hello method to encapsulate:

HelloType HelloInst= new HelloType(hello);

If you prefer you can use the overloaded assignment operator:

HelloType HelloInst = hello;

to create an instance.

Now we can run the original Hello method by calling it directly in the usual way:

int i = hello(2);

or by using the delegate’s invoke method:

int i=HelloInst.Invoke(2);

or by calling the delegate instance as if it was the hello method:

int i=HelloInst(2);

This last form is just a convenience as it implicitly uses the Invoke method.

Notice that the default use of the invoke method makes the delegate instance look like a function object. That is HelloInst is an object but HelloInst(2) looks like a function call. 

Of course there are a few slightly hidden details in these examples which are obvious to experts but often confuse the beginner. The first is that the method being wrapped is still a method belonging to some object and not a "disembodied" function. When the delegate is used to wrap the method the method has to be accessible from where ever the delegate is being declared. In the example above there is the implicit assumption that all of the code is defined within the same class. 

A more elaborate and complete example would create the hello method within a new class:

public class Greetings
{
 public int hello(int param1)
 {
  MessageBox.Show(
    "Hello delegate World"
       + param1.ToString());
  return (++param1);
 }
}

and then within the class that wants to make use of the method:

delegate int HelloType(int param);private void button1_Click(object sender, EventArgs e)
{
 Greetings g = new Greetings();
 HelloType HelloInst = new HelloType(g.hello);
 int i = HelloInst(2);
}

 

Another subtle point is that Invoke uses the same current thread to run the delegate and in this case you need to be aware of any potential “cross threading” problems that might arise.

This also means that the invocation is asynchronous and the calling code will wait until the delegate returns. You can invoke the delegate asynchronously using a thread from the thread pool using BeginInvoke or you could create a thread manually to run the delegate.

To summarise:

To wrap a method belonging to an instance of a class you have to first define a new delegate type with a specific signature and return type, then you wrap the method in and instance of the delegate type. 

 

Banner

<ASIN:1449380344>

<ASIN:1430225378>
<ASIN:0470447613>

<ASIN:1933988924>

<ASIN:3540921443>



Last Updated ( Thursday, 07 January 2016 )