Delegates tutorial meeting specific criteria?

  • Thread starter Thread starter sherifffruitfly
  • Start date Start date
S

sherifffruitfly

I'm looking for a tutorial/sample specifically aimed at showing why
life is better with them than without them. Ideally, what I'd like to
see in the tutorial I'm looking for:

1) No discussion of events.
2) The "non delegates way" and "the delegates way" of dealing with a
problem both presented.
3) A clear explanation of why "the delegates way" is either clearer,
more robust, or more easily maintainable/extensible.
4) One other thing which I'm forgetting atm. :)


I've googled lots, and haven't found even a good approximation to what
I'm looking for.

Thanks for any suggestions!
 
I'm looking for a tutorial/sample specifically aimed at showing why
life is better with them than without them. Ideally, what I'd like to
see in the tutorial I'm looking for:

1) No discussion of events.
2) The "non delegates way" and "the delegates way" of dealing with a
problem both presented.
3) A clear explanation of why "the delegates way" is either clearer,
more robust, or more easily maintainable/extensible.
4) One other thing which I'm forgetting atm. :)

I've googled lots, and haven't found even a good approximation to what
I'm looking for.

Thanks for any suggestions!

Here's one example: a map function.

Suppose you have an array and you want to produce another array by
transforming each element. The "non delegates way" might be a simple
loop:

<code>
int[] input = GetSomeInts();

string[] output = new string[input.Length];
for (int i = 0; i < output.Length; i++)
output = input.ToString();
</code>

Now, suppose you have many places in your program where you do
something similar. You'll end up writing the same loop over and over,
each time giving yourself a chance to introduce a bug. You can avoid
that by using delegates and a general mapping function (and let's use
generics while we're at it):

<code>
TOutput[] Map<TInput,TOutput>(TInput[] array,
Converter<TInput,TOutput> transform)
{
TOutput[] result = new TOutput[array.Length];
for (int i = 0; i < result.Length; i++)
result = transform(array);
return result;
}

// now you can use it like so:
int[] input = GetSomeInts();

string[] output1 = Map<int,string>(input, delegate(int i) { return
i.ToString(); });

int[] output2 = Map<int,int>(input, delegate(int i) { return i *
2; });

double[] output3 = Map<int,double>(input, delegate(int i) { return
Math.Sqrt(i); });
</code>

In this case you've just saved yourself some typing, which is noble
enough in itself.

But suppose you wanted to do something more clever in your mapping...
maybe you're running on a dual core system, and you have a ton of
calculations to do for each element, so you want to spawn a second
thread and process half the array on each core. You don't want to
write that threading code over and over, right? So you write a smart
Map() function once, and then just pass in different delegates to do
different transformations.

I've used delegates in my own production code to do something like
this. I need to do a lot of pixel-by-pixel manipulation of bitmaps,
but the methods that the Bitmap class provides are slow enough to be
unusable. I can access the bitmap data directly by using unsafe code,
but that involves a lot of code. My solution was to write centralized
methods that use unsafe code to access the bitmap data, and pass in
delegates to do each specific color transformation.

In a language without delegates, you'd probably use interfaces or
abstract classes to get the same effect:

<code>
interface IConversion<TInput,TOutput>
{
TOutput Perform(TInput input);
}

TOutput[] Map<TInput,TOutput>(TInput[] array,
IConversion<TInput,TOutput> conversion)
{
TOutput[] result = new TOutput[array.Length];
for (int i = 0; i < result.Length; i++)
result = conversion.Perform(array);
return result;
}
</code>

But then you have to make a new class for each different conversion.
With delegates, you only need a new method. Since C# 2, you don't even
need a method, you can just write the code inline as an anonymous
delegate; and in C# 3 the syntax is even simpler.

Jesse
 
Here's one example: a map function.

Thanks Jesse - that's the kind of thing I'm looking for - I'll study
the example.

Also makes me think of Mathematica - your "Map" looks suspiciously
similar in concept to theirs - I'll check theirs out and see if any
inspiration is to be had.

Thanks again!
 
Thanks Jesse - that's the kind of thing I'm looking for - I'll study
the example.

Also makes me think of Mathematica - your "Map" looks suspiciously
similar in concept to theirs - I'll check theirs out and see if any
inspiration is to be had.

Yeah, it's a common concept. In fact, .NET already includes the method
Array.ConvertAll, which does exactly the same thing.

Some of the other static methods on Array are also good examples of
what delegates are good for: see Exists, Find, TrueForAll, etc. where
you supply a delegate to select the elements you care about.

Jesse
 
I'm looking for a tutorial/sample specifically aimed at showing why
life is better with them than without them. Ideally, what I'd like to
see in the tutorial I'm looking for:

How about LINQ? (in particular, LINQ-to-objects). The following uses
lambda notation, but (in this context) this is just a delegate:

var query = source.Where(x => x.Bar == bar)
.GroupBy(x => x.Category).Select(y => new
{
Name = y.Key,
Total = y.Sum(z => z.Bloop),
Count = y.Count()
});

which is identical to the alternative syntax:

var query2 = from x in source
where x.Bar == bar
group x by x.Category into y
select new
{
Name = y.Key,
Total = y.Sum(z => z.Bloop),
Count = y.Count()
};

This (both: they are identical) is doing a filter with a predicate
(via a delegate), grouping by a selection (via a delegate), and
performing a projection (via a delegate). To do that without delegates
(or expressions) requires looping, running your own filter, worrying
about the dictionary, performing the aggregation etc.

But even better: with this approach, we can swap the filter at runtime
- i.e. if we used:

Func<Foo, bool> filter;
switch(something) {
case [etc]:
filter = whatever;
break;
...
}

We could then use [etc].Where(filter).[etc], and we have a runtime
filter. Very easy and extensible.

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top