Why delegates?

In the past, you could pass a function pointer, cast as an int, and it was up to the function being called to know what type of function pointer was passed. This improved somewhat with later versions of C where the return type of function, number of arguments, and argument type could all be specified. The problem is that it was not type-safe. One example that is used frequently is qsort. This function was passed a void pointer to what was to be sorted, the number of elements to be sorted, the size of the elements, and a comparison function. The call to qsort when you are sorting an array of int might look like this:

int a[100]
// Initialize array a
. . .
qsort((void *)a, 100, sizeof(int), compare);

The comparison routine would look like this:

int compare(const void *a, const void *b)
{
    int ta = *(int *)a;
    int tb = *(int *)b;
    if(ta == tb) return 0;
    else if(ta < tb) return -1;
    else return 1;
}

If some unlucky person happened to see this comparison routine declared, he might be tempted to call it with an array of float, an array of char, or an array of structures for that matter. All would result in an incorrect sort at best and a program crash at worst. A method is needed to tell the compiler about a function that does not exist yet! With templates, C++ solved the problem elegantly. Templates turned the function pointer into a class. These types of classes were called function objects.

With the advent of templates, the following type of declaration became important:

template <class T>
int compare (const T& a, const T& b)
{
     if(a < b) return -1;
     else if(a == b) return 0;
     else return 1;
}

As long as the class that was passed as an argument to the compare function had a < operator and an == operator, this function would work just fine. The problem was that passing this type of function as an argument to another function (such as qsort) was problematic. The function that was to use this compare routine needed to specify what kinds of functions it accepted. The equivalent qsort had to be defined for each type that was sorted. The next step was to create a class or structure that wrapped the functionality of the function. The following code snippet shows how this is done:

template <class T> struct compare
{
public:
  int operator () (T a, T b)
   {
     if(a < b) return -1;
     else if(a == b) return 0;
     else return 1;
   }
};

With Standard Template Library (STL) (as part of the C++ ANSI Standard, you can now call the sort function like this:

compare<int> mycompare;

std::vector<int> v;
v.push_back(1);
v.push_back(2);
std::sort(v.begin(), v.end(), mycompare);

This is essentially what delegates do to function pointers. Delegates wrap a class around the function. By wrapping the function pointer, the code that calls or uses the delegate can be written in a generic fashion. This code also has the added benefit of being type-safe. Another benefit of delegates is that they are part of the language. You can achieve the functionality of delegates with function objects, but that is limited to C++. Delegates that are a part of the .NET Framework are available to any language that is supported by the .NET Framework.

The next section spells out some of the characteristics of this delegate class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.69.143