1,105,578 Community Members

Why use delegates?

Member Avatar
BobLewiston
Junior Poster
105 posts since Nov 2008
Reputation Points: 0 [?]
Q&As Helped to Solve: 0 [?]
Skill Endorsements: 0 [?]
 
0
 

Why use delegates?

I understand HOW to use delegates, but in what situations would you actually use them? The examples I see in the literature are all in situations where the code could just be written without ever using them.

Member Avatar
sknake
Senior Poster
3,957 posts since Feb 2009
Reputation Points: 1,620 [?]
Q&As Helped to Solve: 747 [?]
Skill Endorsements: 25 [?]
Featured
 
0
 

Quote from http://www.akadia.com/services/dotnet_delegates_and_events.html :

A delegate in C# is similar to a function pointer in C or C++. Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code which can call the referenced method, without having to know at compile time which method will be invoked.

Member Avatar
Rashakil Fol
Super Senior Demiposter
2,596 posts since Jun 2005
Reputation Points: 982 [?]
Q&As Helped to Solve: 209 [?]
Skill Endorsements: 42 [?]
Team Colleague
 
2
 

I understand HOW to use delegates, but in what situations would you actually use them?

There are two places in this answer: first, consider any place that calls for an interface of the following form:

interface IBlah {
    Foo JustOneMethod();
}

These interfaces happen all the time.

Instead of defining these classes all over the place, you can just use a delegate of type Func<Foo>. For example, compare IComparer<T> against Comparison<T>.


A second reason to use delegates: to use them in ways that makes low-level code more readable and less risky. Consider the following piece of code, that we see all the time:

List<Blah> blahs = new List<Blah>();
foreach (Bar elem in someOtherList)
{
     blahs.Add(elem.Prop);
}

Disgusting! It is much more readable to write the following:

List<Blah> blahs = someOtherList
    .Select(elem => elem.Prop)
    .ToList();

Consider a similar variant:

List<Blah> blahs = new List<Blah>();
foreach (Bar x in someOtherList)
{
     if (x.Foo())
         blahs.Add(x.Prop);
}

Yuck. It is much more readable to write the following:

List<Blah> blahs = someOtherList
    .Where(x => x.Foo())
    .Select(x => x.Prop)
    .ToList();

The code is faster to read and easier to read because you don't have to decode complicated loops -- or because you don't have to put your code out in all these separate functions to avoid complicated loops, or whatever other things blub programmers do.

It makes no sense to rewrite hard-coded implementations of Where and Select and First and Any and a lot of other higher order functions found in the System.Linq namespace, over and over again. But that's what people do.


Here's a longer example of mostly the first reason.

Suppose you want to sort a list. Suppose you want to sort the elements by comparing the Foo property...

class FooCmp : IComparer<Blah> {
    public int Compare(Blah left, Blah right) {
        return left.Foo.CompareTo(right.Foo);
    }
}

And then use it in your code...

blahs.Sort(new FooCmp());

But wait. What if you wanted to compare by the Bar property? Or both? Or something else? This is just an absurd amount of work. Instead of creating some IComparer<Blah> class, the easy and better solution is to use an inline delegate.

blahs.Sort((left, right) => left.Foo.CompareTo(right.Foo));

And consider how complex such a thing can get. Suppose you want to sort vectors by their magnitude -- but the measurement of the magnitude can change. How are you going to represent measurements of a magnitude? One way is to make an interface:

public interface Normer {
    double Norm(Vector v)
}

And then you can have various implementations:

public class LNNormer : INormer {
    double n;
    double recip;
    public LNNormer(double n) {
        this.n = n;
        recip = 1.0 / n;
    }
    public double Norm(Vector v) {
        return Math.Pow(Math.Pow(v.X, n) + Math.Pow(v.Y, n), recip);
    }
}

public class MaxNorm : INormer {
    public double Norm(Vector v) {
        return Math.Max(v.X, v.Y);
    }
}
// is DiscreteNorm the right name?  I don't know.
public class DiscreteNorm : INormer {
    public double Norm(Vector v) {
        return v.X == 0.0 && v.Y == 0.0 ? 0.0 : 1.0;
    }
}

Now if you wanted to sort based on what Norm you have, you might want to make a comparer...

public class NormComparer : IComparer<Vector> {
    INormer normer;
    public NormComparer(INormer normer) {
        this.normer = normer;
    }
    public int Compare(Vector left, Vector right) {
        return normer.Norm(left).CompareTo(normer.Norm(right));
    }
}

And then, finally, sorting.

public static void SortByMagnitude(List<Vector> vectors, INormer normer) {
    list.Sort(new NormComparer(normer));
}

How cumbersome! This is how Javacoders operate, and it's the reason Java sucks.

Here's how you'd do it using delegates:

// Instead of INormer, have a delegate type Normer.
public delegate double Normer(Vector v);

// Let's make some functions that return normers:
public static Normer LNNormer(double n) {
    double recip = 1.0 / n;
    return v => Math.Pow(Math.Pow(v.X, n) + Math.Pow(v.Y, n), recip);
}

// instead of return a delegate, MaxNorm itself converts to a delegate as needed
public static double MaxNorm(Vector v) {
    return Math.Max(v.X, v.Y);
}
// similarly for DiscreteNorm:
public static double DiscreteNorm(Vector v) {
    return v.X == 0.0 && v.Y == 0.0 ? 0.0 : 1.0;
}

// sorting is easy
public static void SortByMagnitude(List<Vector> vectors, Normer normer)
{
    vectors.Sort((u, v) => normer(u).CompareTo(normer(v)));
}

And using our sort function...

SortByMagnitude(list1, MaxNorm);  // max-norm distance
SortByMagnitude(list2, DiscreteNorm);  // not good for sorting
SortByMagnitude(list3, LNNorm(2));  // euclidean distance
SortByMagnitude(list4, LNNorm(1));  // taxicab distance

Oftentimes, people will go for solutions that require writing the least code -- taking in a numeric parameter to parameterize behavior, for example, instead of a generic delegate. The SortByMagnitude function, when written by a javacoder, would probably take a numeric parameter and run what's equivalent to SortByMagnitude(list, LNNorm(numericParameter)) . It's just too much of a pain to make an INormer interface and children. And so the software becomes inherently less flexible and less powerful -- it can only do one kind of norming. The limitations of Java puts a huge tax on good software design. Having delegates, specifically anonymous functions, with convenient syntax, removes this tax.

About one third of the design patterns in the famous gang of four book are simply different ways of simulating the delegate type Func<T> , and the ideal implementations in C# for all of them involve extensive use of delegates.

Member Avatar
Rashakil Fol
Super Senior Demiposter
2,596 posts since Jun 2005
Reputation Points: 982 [?]
Q&As Helped to Solve: 209 [?]
Skill Endorsements: 42 [?]
Team Colleague
 
0
 

And, you know, that's just such an incomplete answer.

You
This article has been dead for over three months: Start a new discussion instead
Post:
Start New Discussion
Tags Related to this Article