C# School Assignment? Project Due Tomorrow? Chat LIVE With A Programming Expert!

Welcome to Dream.In.Code
Become a C# Expert!

Join 300,358 C# Programmers for FREE! Get instant access to thousands of C# experts, tutorials, code snippets, and more! There are 1,916 people online right now. Registration is fast and FREE... Join Now!




Delegates from the Begining

 
Reply to this topicStart new topic

> Delegates from the Begining, How to effecively use delegates in your own code and why you should.

baavgai
Group Icon



post 11 Oct, 2008 - 01:43 PM
Post #1


Delegates are one of the most versatile, powerful, and often misunderstood features of C#. They are integral to the elegant .NET event system. If you've ever written a custom event, you may have used a delegate, possibly without really understanding the particulars of it. With the introduction of Generics, delegates finally got the respect they deserve.

In spite of all this, delegates are also probably one of the most neglected features of the language. This may be because they crop up at various times with little introduction or explanation and so little understanding. As of this writing, there really seems to be no simple, beginner level guides to their use. Hopefully this article will help fill that gap.

Here's a simple program that prints out some information about an array:
csharp

void PrintArrayInfo(int[] list) {
Console.Write("Number List:");
foreach (int item in list) {
Console.Write(" " + item);
}
Console.WriteLine();

Console.Write("Even Number List:");
foreach (int item in list) {
if (item % 2 == 0) {
Console.Write(" " + item);
}
}
Console.WriteLine();

Console.Write("Half Number List:");
foreach (int item in list) {
Console.Write(" " + item/2);
}
Console.WriteLine();
}


We see three chunks of basically the same code. At this point, our inner programmer should be trying to figure out the common elements in those three chunks. Their should be a way... but there's not. Because the differing element is inside the repeating code.

We're going to identify the elements inside and give them their own methods.

csharp

void ShowItem(int item) {
Console.Write(" " + item);
}

void ShowEvenItem(int item) {
if (item % 2 == 0) {
Console.Write(" " + item);
}
}

void ShowHalfItem(int item) {
Console.Write(" " + item/2);
}

void PrintArrayInfo(int[] list) {
Console.Write("Number List:");
foreach (int item in list) {
ShowItem(item);
}
Console.WriteLine();

Console.Write("Even Number List:");
foreach (int item in list) {
ShowEvenItem(item);
}
Console.WriteLine();

Console.Write("Half Number List:");
foreach (int item in list) {
ShowHalfItem(item);
}
Console.WriteLine();
}


Well, that didn't seem to gain us anything. In fact, it looks worse than when we started. But it does illustrate a point. If only we could somehow pass a reference to each of those methods, we'd be onto something. This is where delegates come in.

A delegate is simply a place holder for a method. You can think of it as a method type or a method interface. Here's what it looks like in action.

csharp

// We declare our delegate
// it's called ProcessListItem and defines a method
// that takes one parameter and has a void return value
delegate void ProcessListItem(int item);

// This method takes a name, a list, a method ( delegate )
void ProcessList(string name, int[] list, ProcessListItem processor) {
Console.Write(name + ":");
foreach (int item in list) {
// we use the label "processor"
// defined as a delegate of ProcessListItem
// we call it as if it were simply a method.
processor(item);
}
Console.WriteLine();
}

void PrintArrayInfo(int[] list) {
// make calls, passing predefined methods you want to use

// you can use just the name
ProcessList("Number List", list, ShowItem);
ProcessList("Even Number List", list, ShowEvenItem);

// we can also explicitly reference the delegate you're
// using by name, with this syntax
ProcessList("Half Number List", list,
new ProcessListItem(ShowHalfItem)
);
}


This is neat, but it gets better. One thing you may be thinking is what a bother it seems to have to define all those methods for passing. Often we just want a one off. For this, we can use an anonymous delegate.

csharp

ProcessList("Odd Number List", list, delegate(int item) {
if (item % 2 != 0) {
Console.Write(" " + item);
}
});


But anonymous delegates offer more than simply saving a method call. They are the secret to doing to unexpected and very useful things. Here's an example:

csharp

int maxNum = list[0];
ProcessList("Finding Max", list, delegate(int item) {
Console.Write(".");
if (item > maxNum) {
maxNum = item;
}
});
Console.WriteLine("Max = " + maxNum);


Look at the above carefully. We define a local variable maxNum. Inside our anonymous delegate we reference that variable because it's in our current scope! Without breaking the rules of our delegate method pattern, we have effectively managed to pass extra arguments in and out of it.

That's it for now. We'll explore that last bit in detail when we talk about the powerful use of delegates in the Generic collections.

Here's a complete example of the above in one place. ( Thanks for RodgerB for the catch. )

csharp

using System;

namespace DelegateDemo {
/// <summary>
/// A simple demonstration of using delegates
/// </summary>
class Program {

// We declare our delegate
// it's called ProcessListItem and defines a method
// that takes one parameter and a a void return value
private delegate void ProcessListItem(int item);

// This method takes a name, a list, a method ( delegate )
private void ProcessList(string name, int[] list, ProcessListItem processor) {
Console.Write(name + ":");
foreach (int item in list) {
// we use the label "processor"
// defined as a delegate of ProcessListItem
// we call it as if it were simply a method.
processor(item);
}
Console.WriteLine();
}

public void PrintArrayInfo(int[] list) {
// you can use just predefined methods
ProcessList("Number List", list, this.ShowItem);
ProcessList("Even Number List", list, this.ShowEvenItem);

// you can also explicitly reference the delegate you're
// using by name, with this syntax
ProcessList("Half Number List", list,
new ProcessListItem(ShowHalfItem)
);

// you can use an anonymous delegate
ProcessList("Odd Number List", list, delegate(int item) {
if (item % 2 != 0) {
Console.Write(" " + item);
}
});

// with an anonymous delegate,
// you can also process other values in the local scope
int maxNum = list[0];
ProcessList("Finding Max", list, delegate(int item) {
Console.Write(".");
if (item > maxNum) {
maxNum = item;
}
});
Console.WriteLine("Max = " + maxNum);
}


private void ShowItem(int item) {
Console.Write(" " + item);
}

private void ShowEvenItem(int item) {
if (item % 2 == 0) {
Console.Write(" " + item);
}
}

private void ShowHalfItem(int item) {
Console.Write(" " + item / 2.0);
}

static void Main(string[] args) {
Program pgm = new Program();
// test data
pgm.PrintArrayInfo(new int[] { 2, 5, 8, 9, 5, 0, 8, 6, 7, 3 });
Console.ReadLine();
}
}
}
/* Results:
Number List: 2 5 8 9 5 0 8 6 7 3
Even Number List: 2 8 0 8 6
Half Number List: 1 2.5 4 4.5 2.5 0 4 3 3.5 1.5
Odd Number List: 5 9 5 7 3
Finding Max:..........
Max = 9
*/


This post has been edited by baavgai: 12 Oct, 2008 - 04:11 AM
Go to the top of the page
+Quote Post


Register to Make This Ad Go Away!

RodgerB
Group Icon



post 12 Oct, 2008 - 01:39 AM
Post #2
Thanks for an excellent tutorial, this should be featured. Most of the time I stuggle with grasping the why concept of programming when I'm learning a new skill.

It's easy to show how to do it, it's easy to replicate it, but people rarely explain why you do it, which is most important to me and hopefully everyone elses learning.

Here is an example implementation for the rushed busy programmers busy enough to be browsing </dreams.code>.

(Actually, ignore the divide by zero check, I wasn't awake wink2.gif).
Attached File  Program.txt ( 1.45k ) Number of downloads: 83


This post has been edited by RodgerB: 12 Oct, 2008 - 02:11 AM
Go to the top of the page
+Quote Post

baavgai
Group Icon



post 12 Oct, 2008 - 02:15 AM
Post #3
Thanks. I had intended to include a cut and paste, play at home version, but forgot. Fixed now.
Go to the top of the page
+Quote Post

Footsie
Group Icon



post 14 Oct, 2008 - 03:27 AM
Post #4
Maybe you can add an illustration of how a delegate would work in the real world (non-programming example)? As a beginner, I always find it easier to think in 'real' concepts.

Still a great tutorial of the usefulness of delegates.
Thank you.
Go to the top of the page
+Quote Post

baavgai
Group Icon



post 14 Oct, 2008 - 04:36 AM
Post #5
QUOTE(Footsie @ 14 Oct, 2008 - 07:27 AM) *

how a delegate would work in the real world (non-programming example)?


?!? It's a language construct, peculiar to programming, there is no real world...

Oh well, here goes: Let's imagine a method is a machine that grinds grain into flower. The method takes grain as a parameter, returns final product. Normally, we have no real control of the workings of the machine. It sifts, it moves, it grinds, it passes back product. Imagine the grinder part of the machine is modular, allowing the swapping in of different wheels. A delegate allows you to pass the machine another machine part, the wheel, to use.

Sorry if that makes little sense. When you're talking about things like static constructs or function pointers there really are no "non-programming" examples.
Go to the top of the page
+Quote Post

Footsie
Group Icon



post 14 Oct, 2008 - 01:25 PM
Post #6
QUOTE(baavgai @ 14 Oct, 2008 - 02:36 PM) *

?!? It's a language construct, peculiar to programming, there is no real world...


Understood.

It's just that delegates have always bothered me a bit. So I try and look at them from all angles to get a handle on them.
smile.gif Thanks for your description.
Go to the top of the page
+Quote Post

baavgai
Group Icon



post 20 Oct, 2008 - 05:48 PM
Post #7
Another way to express that last find max:

csharp

// declare a local variable
// set it equal the the first value in the list
int maxNum = list[0];
// declare a delegate reference
ProcessListItem findMaxMethod;

// create a reference from an anonymous delegate
findMaxMethod =
// use the new syntax
new ProcessListItem(
// the delegate must match the pattern void(int)
// an anonymous delegate doesn't declare a return type
// but does declare argument lists
delegate(int item) {
// everything inside here behaves like the block
// inside any normal method
Console.Write(".");
// of very special note here, maxNum behaves
// as if it were a global variable for this method
// due to it being declared within the blox context
if (item > maxNum) { maxNum = item; }
} // close the delgate block
); // close the new for the process item

// make the call, give it the delegate reference
// we've already prepared
ProcessList("Finding Max", list, findMaxMethod);
Console.WriteLine("Max = " + maxNum);



This post has been edited by baavgai: 20 Oct, 2008 - 05:49 PM
Go to the top of the page
+Quote Post


Fast ReplyReply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 


Lo-Fi Version Time is now: 11/7/09 07:37PM

Live C# Help!

Be Social

Dream.In.Code RSS Feed Dream.In.Code LinkedIn Group Follow Us On Twitter Fan Us On Facebook

C# Tutorials

Reference Sheets

C# Snippets

DIC Chatroom

Bye Bye Ads

Monthly Drawing

Thumb Drive

Top Contributors

Top 10 Kudos This Month