27

I am trying to learn how to do events in C#, and according to the MSDN Events tutorial,

Events are declared using delegates. If you have not yet studied the Delegates Tutorial, you should do so before continuing

so now I'm trying to understand Delegates first, and failing horribly. What are they for? What problem are you trying to solve where you delegate something?

I THINK they're for passing methods into other methods but what is the point of that? Why not just have a method that does whatever you're doing in the first place?

  • 6
    Are you familiar with lambdas? (foo, bar) => { /* code */ }? Those are the same thing as delegates. Just in case that wasn't apparent. – KChaloux Sep 12 '13 at 13:42
  • 2
    It isn't specific to delegates or even C#, but Joel S gives a really good case for functional programming here: joelonsoftware.com/items/2006/08/01.html – vaughandroid Sep 12 '13 at 14:39
  • 1
    @gnat - If anything, I'd say his confusion came from the connection between delegates and events; "what is it about events that makes delegates necessary / useful?" I think that to some people, early personal theories could make them think you could have subclasses override an "onClick()" method to accomplish event behavior. – Katana314 Sep 12 '13 at 15:10
  • 1
    @Katana314 as far as I can tell, most of the answerers didn't read that comment (and rightfully so, because comments are ephemeral / inessential compared to what is written in the answer). As a result, what I see is quick piling of the answers that only make it closer match to that "Real world use of C# Delegates" -- making your, or mine, or asker's beliefs as ephemeral / inessential as that comment oh-not-a-dupe – gnat Sep 12 '13 at 15:20
  • 1
    @Katana314 "His question is about events, as he says directly in the question title" What? No! My question is NOT about events, as it says directly in the title! "Why would I ever use delegates if I'm NOT DOING EVENTS?" is the title. – medivh Sep 13 '13 at 8:18
59

Passing functions to other functions is a very good way of generalizing code, especially when you have lots of nearly-duplicate code with only the logic in the middle differing.

My favorite (simple) example of this is making a Benchmark function, that accepts a delegate containing the code you wish to benchmark. I'll be using lambda syntax and Func/Action objects, because they're much more concise (and common) than seeing explicitly created delegates nowadays.

public void Benchmark(int times, Action func)
{
    var watch = new Stopwatch();
    double totalTime = 0.0;

    for (int i = 0; i < times; i++)
    {
        watch.Start();
        func(); // Execute our injected function
        watch.Stop();

        totalTime += watch.EllapsedTimeMilliseconds;
        watch.Reset();
    }

    double averageTime = totalTime / times;
    Console.WriteLine("{0}ms", averageTime);
}

You can now pass in any block of code to that benchmark function (along with the number of times you want to run it) and get back the average execution time!

// Benchmark the amount of time it takes to ToList a range of 100,000 items
Benchmark(5, () =>
{
    var xs = Enumerable.Range(0, 100000).ToList();
});

// You can also pass in any void Function() by its handler
Benchmark(5, SomeExpensiveFunction);

If you couldn't inject the code you wanted to benchmark into the middle of that function, you'd probably end up copying and pasting the logic around anywhere that you wanted to use it.

Linq makes extensive use of function passing, allowing you to have a whole host of really flexible operations on sets of data. Let's take the Where function as an example. It filter's out elements of a list that return "true" when passed a comparison function.

var xs = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// This function compares each element, and returns true for those that are above 5
//   Result = { 6, 7, 8, 9, 10 }
var above5 = xs.Where((x) => x > 5);

// This function only returns true if an element is even
//   Result = { 2, 4, 6, 8, 10 }
var evens = xs.Where((x) => x % 2 == 0);

The Where function itself very generalized. It simply loops over a collection, and yields a new collection containing only the values that match some predicate function. It's up to you to inject the code that tells it precisely what it's looking for.

| improve this answer | |
  • 4
    Maybe I just got it. I need to think on this a little, thank you. – medivh Sep 12 '13 at 13:54
  • 1
    @medivh Hope it's helpful! Do check out Linq. It has a lot of very useful collection manipulation functions, almost all of which use lambdas to define specific behavior. – KChaloux Sep 12 '13 at 13:59
11

Suppose you have a complicated mathematical calculation to do, that involves performing rounding. Suppose that sometimes you want the rounding to be round to even (aka bankers' rounding), but other times you want round away from zero (aka 'round up [in absolute value]').

Now, you could write two methods:

double DoTheCalculationWithRoundToEven(double input1, double input2)
{
    var intermediate1 = Math.Pow(input1, input2);
    // more calculations...
    var intermediate5 = Math.Round(intermediate4, MidpointRounding.ToEven);
    // more calculations...
    var intermediate10 = Math.Abs(intermediate9);

    return intermediate10;
}

double DoTheCalculationWithRoundAwayFromZero(double input1, double input2)
{
    var intermediate1 = Math.Pow(input1, input2);
    // more calculations...
    var intermediate5 = Math.Round(intermediate4, MidpointRounding.AwayFromZero);
    // more calculations...
    var intermediate10 = Math.Abs(intermediate9);

    return intermediate10;
}

with the two methods being the same except for that one line in the middle.

Now, this works, BUT you've got duplicated code and the very real possibility that some future maintainer (for example, a future you) will change one version of the intermediate2 to intermediate3 and forget about the other.

A delegate type and parameter can solve this duplication.

First declare the delegate type:

    public delegate double Rounder(double value);

This says that a Rounder is a method that accepts a double and returns a double.

Next, have one calculation method that accepts a Rounder and uses it to round:

double DoTheCalculation(double input1, double input2, Rounder rounder)
{
    var intermediate1 = Math.Pow(input1, input2);
    // more calculations...
    var intermediate5 = rounder(intermediate4);
    // more calculations...
    var intermediate10 = Math.Abs(intermediate9);

    return intermediate10;
}

Finally, when you want the calculation done, pass an appropriate Rounder. You can spell it all out with methods:

double RounderAwayFromZero(double value)
{
    return Math.Round(value, MidpointRounding.AwayFromZero);
}
// same for ToEven

then call one of:

var result = DoTheCalculation(input1, input2, RounderAwayFromZero);

or

var result = DoTheCalculation(input1, input2, RounderToEven);

but C# allows you to do this all inline with lambdas:

var result = DoTheCalculation(input1, input2, 
     value => Math.Round(value, MidpointRounding.AwayFromZero));

where the lambda expression defines a method with the correct signature to be a Rounder.

| improve this answer | |
  • So you never write a function definition for public delegate double Rounder(double value); then? I think that may have been one of the places I felt very stuck. – medivh Sep 12 '13 at 14:08
  • @medivh Writing out delegates explicitly is a bit of an artifact from C# 2.0 I think. You can use lambda syntax pretty much everywhere now, and use Func<T1, T2, ...> or Action<T1, T2, ...> objects to represent the types of functions you want to pass around – KChaloux Sep 12 '13 at 14:11
  • @medivh it can be confusing because people are generally fairly loose with terminology: the word "delegate" is used to refer both to the type (the signature that must be matched, sort of) and instances (the actual methods / lambdas that do the work), and there's also the specific framework type System.Delegate ! – AakashM Sep 12 '13 at 14:15
  • 2
    @medivh Remember, action and func are delegates, they're just delegates that have been defined in the standard library with generics so you don't have to define them yourself, they are as: public delegate void Action<T>(T input) and there's another one public delegate void Action<T1, T2>(T1 input1, T2 input2) and so on, and actions are defined in the .NET framework as public delegate U Func<T,U>(T input) and so on. The lambda syntax is simply an expression syntax that the compiler converts into the standard action/func delegates. – Jimmy Hoffa Sep 12 '13 at 15:00
  • @AakashM Thanks for this answer, it's helped to understand lambdas much better than the "you use them in LINQ and TPL" understanding that I had before! – Brian Snow Sep 12 '13 at 15:11
1

Delegates act as a way to parametrize some kind of action. The function that is being passed a delegate doesn't care how the action is done, just that it is performed.

For example, look at the IComparer interface. It allows you to sort a list without relying on how the type in the list is "naturally" sorted. However, when you look at the interface, it only defines one method. It seams rather wasteful to define a whole class that only implements a single function.

Delegates solve this problem1. You won't need to define a class, just the method. It allows you to remove boilerplate code and focus on what you want to do. Lambdas take this a step forward so that the function definition can be in-lined instead of defined somewhere else in the class.

1 If you look at Sort(), an overloaded version exists that uses a delegate.

| improve this answer | |
  • Coming from a C background, this seems back-asswards to me. I thought the entire point of C# was that you couldn't create methods outside of classes - so now they put in a keyword so you can do it again? – medivh Sep 12 '13 at 13:59
  • 3
    @medivh: C# is still an object oriented language. However as it has evolved, it has started to borrow concepts from functional languages to make things nicer to work with. At a technical level, it is still a class, the syntax just allows you to write just the code that matters instead of the boilerplate. If you go back to the origins, the concept of an object oriented language had less to do with classes and more to do with message passing (see Smalltalk) – unholysampler Sep 12 '13 at 14:13
  • @medivh “the entire point of C# was that you couldn't create methods outside of classes” That's a relatively minor point. C# has dozens other features that are much more important than that. And each lambda is still inside a class. – svick Sep 12 '13 at 16:39
1

Delegates are similar to interfaces. You are simply defining a contract which can be fulfilled by whatever is consuming the class that uses the delegate. The big difference between an interface and a delegate is an interface defines a contract for an entire class while a delegate defines a contract for a single method.

The best, most basic real world example of using delegates is the "Hole in the Middle" pattern. I don't need this pattern very often, but when I do, it's extremely valuable. You can accomplish this same pattern using inheritance and abstract methods, however, delegates allow you to accomplish this using composition.

public delegate void MyDelegate(string param1, string param2);

public void SomeMethod(string param1, string param2) {
    Console.WriteLine(param1);
    //do some more work
}

public void DoSomething(MyDelegate somemethod) {
    //do something here
    somemethod(param1, param2);
    //do something else here
}
| improve this answer | |
1

The purpose of passing a method to another method is to separate the purposes of those two methods. This is in keeping with accepted principles of object-oriented design, specifically the Single Responsibility Principle: "a code object should do one thing, and be the only code object in the codebase that does that thing".

Case in point, let's say you have a method that reads a file in a certain format (let's say pipe-delimited text), and a method that digests the information in that file in a specific way to extract some information. You say you should put these together in one method. Well, what happens when the program needs to start reading from CSV files? What happens when you need a different subset of the data contained in it? You need to change the method. But the program still needs to handle pipe-delimited text and pull the older set of data. Now you have one method that reads from two different file types and produces two different sets of data. This method is rapidly on its way to becoming unmaintainable.

Instead, you can create a method that reads pipe-delimited text, and another that reads CSVs. You can create a method that extracts one data set, and another that extracts the second. Then, you can give the file-reading method as a delegate to the data extraction method, or vice versa. Or, you can give them both to a third class of method that will simply execute them in order, giving the data from the file to the dataset-extracting method.

By dong this, you increase the number of methods you're writing, but each method has one and only one purpose. Any change you need to make so that the method works properly won't affect the purpose or execution of any other. The resulting code is easier to understand, easier to maintain, and thus will require less testing in order to ensure its correctness.

| improve this answer | |
0

There are a ton of framework class library types with operations that take delegates as a parameter.

If you use the Task parallel Library, LINQ or Entity Framework, you will be passing in delegates as arguments.

A lot of the delegate signatures you would want to use are already defined (Action, Func, Predicate) and the syntax made simpler with lambda expressions.

| improve this answer | |
  • I know they exist - I get that the answer to "Why should I learn them" is "Because you need them to interact with stuff that requires them." My question is "Why does stuff require them in the first place? What code are you writing where you think 'You know what I need here? A delegate!'?" – medivh Sep 12 '13 at 13:48
  • Because there is no other way to pass a piece of code to a method in C#. The APIs exposed in those 3 libraries are some of the best, in my opinion. Try to imagine how you would make a similar library without delegates (aside from true first-class functions); you would not be able to. msdn.microsoft.com/en-us/library/dd460720.aspx Try rewriting Parallel.ForEach without delegates. – brian Sep 12 '13 at 13:53
  • I could write "Parallel.ForEach" without delegates easy, but not in C#. There is some issue with C# that C++ doesn't have because I wouldn't need a delegate to do it in C++. – medivh Sep 12 '13 at 14:01
  • stackoverflow.com/questions/10766139/… great explanation there. Although, I prefer F#'s first class function system, C#'s is easier to understand once you come to terms with HAVING to declare the delegate signature and name it OR using a "built in" one. – brian Sep 12 '13 at 14:11
  • @medivh What would you use? Functors? Functors in C++ and delegates in C# fulfill the same role (even if the technicalities are different). – svick Sep 12 '13 at 16:42
0

Probably the simplest way to understand delegates and events is to look at GUI programming. Let's take a very common case: you have a form with a button on it, and when the user clicks the button, you want a certain piece of code to execute.

The guy who wrote the button class has no idea what you want to do with it, obviously. So he can't build your code for "what this button should do" into his class. And even if he could, he really shouldn't, because then it stops being a "GUI button" and turns into a "Medivh's this-specific-use-case button" which is much less useful in the general case.

So instead, the guy who wrote the button class put in an Event, which is sort of a hook into the functionality of the object. An Event is basically a piece of the code that's being executed where someone else can stick code in there. So when someone clicks the button, it fires the OnClick event, and if you've attached anything to that event--such as a method (a delegate) that causes a new form to open--then that delegate will be executed at this point.

When you start thinking of methods as not just a procedure or function you can call, but an object you can pass to other code, it opens up all sorts of new possibilities. Some of the other people here have already mentioned LINQ and the Task Parallel Library, which contain algorithms that define the basic idea of something, and then let you pass in a delegate to fill in the details.

| improve this answer | |

Not the answer you're looking for? Browse other questions tagged or ask your own question.