Proper use of delegates.

  • Thread starter Thread starter Roger Frost
  • Start date Start date
The reason for using the Converter<TInput, TOutput> delegate type is that
you've got a function that converts things. It should actually _convert_
a customer to an integer. I would not expect someone to use the
Converter<TInput, TOutput> type for a situation where you're simply
returning some value based on some input. The Func<T, TResult> delegate
type would be much more appropriate, and makes it much more clear that
you're dealing with a method that takes a single parameter and returns
some new value based on, but not equivalent to, that parameter.

Yes - this confusion is partly the fault of the badly named method
So yes, of course your examples are difficult to understand. But that's
because you used the wrong type. Not because you should have created a
brand new type. Using Func<T, TResult> would have been much less
misleading.

The other issue is that the delegate type should describe the _type_. The
variable should describe the _use_. If you're having trouble figuring out
the difference between a parameter named "ca" and a parameter named "cp",
it's not the fault of the type you used. It's the fault of the variable
names you chose.

You don't go around declaring new types for every different way you're
going to use an integer (I hope). Likewise, there's no reason to go
around declaring new types for every different way you're going to have a
method that takes an ICustomer as input and returns an integer as output.

I certainly agree that self-documenting code is a desirable goal. But
there's a right way and a wrong way to go about that. Your examples don't
demonstrate the right way.

I'd just like to assert that, contrary to the evidence in this thread,
Peter and I really are different people :)
 
[...]
Sure, my suggestion still doesn't protect you completely from
mistakes, but to me it makes your code more self-documenting, which is
important enough for me.

Actually, both of your examples are flawed, in their execution and thus  
subsequently your analysis.

The reason for using the Converter<TInput, TOutput> delegate type is that  
you've got a function that converts things.  It should actually _convert_  
a customer to an integer.  I would not expect someone to use the  
Converter<TInput, TOutput> type for a situation where you're simply  
returning some value based on some input.  The Func<T, TResult> delegate 
type would be much more appropriate, and makes it much more clear that  
you're dealing with a method that takes a single parameter and returns  
some new value based on, but not equivalent to, that parameter.

So yes, of course your examples are difficult to understand.  But that's 
because you used the wrong type.  Not because you should have created a  
brand new type.  Using Func<T, TResult> would have been much less  
misleading.

The other issue is that the delegate type should describe the _type_.  The  
variable should describe the _use_.  If you're having trouble figuring out  
the difference between a parameter named "ca" and a parameter named "cp",  
it's not the fault of the type you used.  It's the fault of the variable 
names you chose.

You don't go around declaring new types for every different way you're  
going to use an integer (I hope).  Likewise, there's no reason to go  
around declaring new types for every different way you're going to have a  
method that takes an ICustomer as input and returns an integer as output.

I certainly agree that self-documenting code is a desirable goal.  But  
there's a right way and a wrong way to go about that.  Your examples don't  
demonstrate the right way.

Pete

Func<T, U> is fairly new. It may describe a generic function, but it
still doesn't say what it does. It just saves you from misleading your
callers. And yes, the variables should also be better named. Why say a
variable should have good name but a delegate not? That sounds biased
to me. It is not like you are creating a whole new type.

You really can't compare an integer to a delegate. They do completely
different things. One is much more abstract than the other. Integers
usually act as counters, calculation storage and/or indexes, their use
is widely known and easy to comprehend. That is not true for classes,
delegates and other complex types and concepts. Anyone can create a
class that has 2 integers and 3 doubles and pass it around "treating"
it like it were 12 different types, but just giving them pretty
parameter names isn't clear enough in some situations. I say if
something has a different purpose it should be made its own entity
altogether. Delegates give you the benefit that they take almost no or
no space at all.

Delegates are confusing to most developers. Few people use them for
anything. Many OOP experts say they shouldn't be used at all! If you
are going to use them, make it easy for someone who isn't familiar
with them. Have types and variable names that are intuitive. You
wouldn't argue if we were talking about a class, what makes a delegate
an exception?

Also remember that people unfamiliar with functional programming are
generally confused passing a Comparison<T> to a Sort algorithm. Since
they don't know how the method is using the delegate, they don't know
what the return value should be. -1, 0, 1 or 0, -1, 1, or -2, 0, 53???
Imagine if they didn't even know it was a comparison! Func<T, T,
int> . . . Um . . . what do I do here? You can document a delegate
type you create, where you can't do anything but give a name to a
Funct<T, T, int>! You can document it in one place instead of having
to redefine its purpose in every method that uses it. How can argue
with that?

I'm not sure why you always get so hot blooded anytime someone
disagrees with you. It is a reoccuring thing with you. It isn't that
I'm not hearing what you are saying. It is just that my opinion is
different than yours and you haven't given me a reason to think it is
otherwise.
 
If we pushed your logic further, we shouldn't use "int" in your example
either. After all, that's just a number - it doesn't indicate its use
at all. Instead, following your logic, we should define an Age type and
a CustomerPurchase type, and possibly a Total type, all of which just
encapsulate ints.

This is one of the main reasons why they introduced typedef in C++,
outside of long template names.

If you today decide you want to represent someone's age with an
integer, create a typedef called Age:

typedef int Age;

Three weeks down the age you find you suddenly need to know partial
ages, so you switch to a double:

typedef double Age;

Most of your changes just got done with a single word! That's not
amazing, that's good foresight and coding.

You may think it is tedious to do something like that, and it is in
C#, but it has been shown to save time in many projects.

There are many programming languages available that let you do value
constraints on primitive types, so that numbers have to fall within
certain ranges. That's why we have enums. When your language doesn't
provide such constructs, you make due. You document and you check
rigorously. You can check an integer, but you can't check a delegate.
So you document even more and try to make it as obvious as possible
for the person using your method that the delegate does what you
expect it to.

I am sorry to argue with you two. I see not creating a new delegate
type (which requires about 3 seconds to type) similar to not
documenting. There is always a point in code where being obnoxious
about preventing errors goes too far. But we should try to find that
middle ground. There is being obsessive and then there is being lazy.
I feel as though always reusing existing delegates, at the cost of
potential confusion, is being lazy.

You can document all day long, but most developers first look at the
name of the method they are calling, then the arguments. If the
arguments seem intuitive enough, no more reading is done. If one of
your parameters is misleading, even a little, all the documentation in
the world may not save you. The easier I make my code to use the more
likely it will be a success. I have to be the judge of whether
creating a new delegate will help clarify things. I think in many
scenarios, spending the little extra time to create a new delegate
will make things easier for callers. Leave the generic names for
generic methods, use concrete delegates for concrete methods.

You guys are making good points. I am just a little religious about
coding.
 
I am sorry to argue with you two. I see not creating a new delegate
type (which requires about 3 seconds to type) similar to not
documenting.

The cost of typing is almost always insignificant when deciding on the
most maintainable code.
There is always a point in code where being obnoxious
about preventing errors goes too far. But we should try to find that
middle ground. There is being obsessive and then there is being lazy.
I feel as though always reusing existing delegates, at the cost of
potential confusion, is being lazy.

Whereas I see it as reusing something which the developer should
already be familiar with, rather than introducing another type
unnecessarily.
You can document all day long, but most developers first look at the
name of the method they are calling, then the arguments. If the
arguments seem intuitive enough, no more reading is done. If one of
your parameters is misleading, even a little, all the documentation in
the world may not save you.

Surely that goes for the name of the delegate just as easily as the
name of the parameter - why would you assume that the parameter name is
inappropriate but not the delegate name?

Using a general delegate type but a specific parameter name, there's
less room for contradiction. The type indicates what it is (e.g. a
function taking a Person and returning an integer) and the parameter
name indicates what it's used for (e.g. finding the person's age in
years). Why duplicate the information?
The easier I make my code to use the more
likely it will be a success. I have to be the judge of whether
creating a new delegate will help clarify things. I think in many
scenarios, spending the little extra time to create a new delegate
will make things easier for callers. Leave the generic names for
generic methods, use concrete delegates for concrete methods.

If I use a delegate Func<Person,int> then any .NET 3.5 developer worth
their salt will already know what I'm talking about.

If I introduce a new delegate type, the developer will need to look it
up in order to find out what signature is expected.

How is requiring more work making things *easier* for callers?
You guys are making good points. I am just a little religious about
coding.

Me too - but that doesn't mean going to unnecessary trouble which
introduces more complexity for no gain, from my perspective.
 
The cost of typing is almost always insignificant when deciding on the
most maintainable code.


Whereas I see it as reusing something which the developer should
already be familiar with, rather than introducing another type
unnecessarily.


Perhaps here is our difference. I have no problem introducing a new
type if I believe it will clarify something. If it has the opposite
affect, I won't do it. Perhaps in my world creating new types helps
while in your world it doesn't. Environmental influences it seems.

Surely that goes for the name of the delegate just as easily as the
name of the parameter - why would you assume that the parameter name is
inappropriate but not the delegate name?

Unless you are very generic, a delegate name can be misleading. Once
you become completely generic you rely on parameter documentation and
good variable names. In the real world neither of these are common.
Perhaps, in a controlled environment with strick documentation rules,
this can be remedied. In my environment, coworkers slop code together
as fast as humanly possible, often not commenting and using classes,
etc. inappropriately or in unclear ways.

I see naming *some* delegates as a way of getting readable code
without comments. Especially if I write the library with the delegates
in the interface. I am telling them that they WILL use my delegate and
and it is called this and, yes, they do have to go look at what it
should do. No better way to stop a rampaging developer than to make
him look how to do it right. Although in my environment, I am usually
being called to their desk to explain it. That works for me. I prefer
to be annoyed regularly with questions than with bug fixes. They still
find ways to mangle things, but things like this certainly help.

If I am developing the library, I have the responsibility to make
delegate names appropriate. It is a design flaw if users of my library
have to work hard to understand my code. A class should be easy to use
correctly and hard to use incorrectly. If I put a bad delegate name
out there, that is my problem. A great way of preventing misleading
delegate names is to create specific ones that say what they should be
doing. I'm not going to reuse Converter<T, U> if it doesn't make
sense. Func<T, U> makes less sense when you look at a concrete class.
You don't throw an Exception, you throw a MySpecialException. True, in
the case of delegates it all turns into the same thing at the end, but
for me it is a chance to take some control.

Part of my job is fighting bugs, and preventing them is probably my
biggest task. It is like a rookie cyber security officer - they just
want to shut everything off because there is always some risk factor
involved. As they get more experienced with the needs of the users,
they loosen up and learn to mitigate small risks and find a balance
between paranoia and relaxation. We get hit with a lot of bugs (not
always the developer's fault). Since I am trained to mitigate risks, I
tend to be a little more rigid than most developers. But, I have
pumped out many successful libraries that are intuitive and (so far)
bug free. It has already shown improvements in our workflow and it is
all the more reason for me to keep being as rigid. I have a bad habit
of reading books about good programming practice and appling them
vigorously to everything I do. I think very abstractly, so I tend to
bend everything to the same concepts. To me, delegate names fall under
the same lines of good class, variable and method names. There are no
special exceptions. There nothing I hate more than this:

Customer c = new Customer();

Sure, you know c is a customer, because teh declaration tells you that
is what it is. But, you move down three lines and you are completely
lost again. Instead of c, call it customer. This is also annoying:

C customer = new C();

Sure, you know customer is a customer. You hope customer is a
customer . . . What the heck is a C? You have to look at the
definition of C to see what C is. See? With Func<T, U>, you can't even
look at the definition of Func<T, U> anymore. You have to look at how
it is being used. Have you ever tried to understand an generic
algorithm without writing it? It is hard! For instance, a big
discussion we had a few weeks ago over one of my libraries came up
after a refactoring took place.

Initially, I had created a delegate that looked like this:

delegate V Func<T, U, V>(T lhs, U rhs);

I would write method like int BinarySearch(IList<T> list, Func<T, T,
int> comparison) and give it to my coworkers.

Well, this is the same as Comparison<T>. However, the specific name is
a *lot* clearer. It doesn't matter what I called that variable, it
still confused my coworkers. They had no where to go to except to me.
That is BAD. I could be hit by a bus at any minute! When I replaced it
with Comparison<T>, the questions stopped coming. They had the
Conversion<T> delegate to look at for documentation.

Another coworker of mine originally had about 40 methods that took
Predicate<T, T> which he defined himself. It returned whether the left
hand side was less than the right hand side. He was used to how C++
did comparisons. We talked and he modified his methods to use
Comparison<T>. However, he didn't change the variable name from
lessThanPredicate. So all these Comparison were named
lessThanPredicate.

Even worse, he had other Predicate<T, T> that expected true if they
were equal! He called all those equalToPredicate. But, being my fault,
he had changed all those to Comparison<T> also. He was so used to
replacing lessThanPredicate(lhs, rhs) to lessThanPredicate(lhs, rhs) <
0 that when he got to the equality ones, he total spaced and did the
same. None of his code was working. We had to go through all 40
methods to figure out, after the fact, which ones where meant to be
equals and which were meant to be comparison. After all that I told
him to create a new delegate called bool EqualityComparison<T>(T lhs,
T rhs). That kicked it in the butt altogether.

Here creating a better name would have prevented the error altogether.
Variable names were misleading because they weren't changed with their
types. In the beginning all we had was Func<T, T, bool> and look how
we managed to turn it into a Comparison<T> and a
EqualityComparison<T>. Is that convincing enough? It is for me!

The same guy told me that while he was developing, he changed the name
from Predicate to BinaryPredicate and back again. He had an overloaded
predicate that took two different types. He had a lot of Predicates! I
think more confusing than anything is seeing Predicate<T, T> being
used for less than in 20 methods and equal to in the other 20. I
wouldn't know whether to trust the delegate name or the parameter
name. Furthermore, since the delegates are the same, you could pass an
"equal to" delegate to a "less than" delegate and everything will
compile. Using specific names forces a compile time check. You
actually have to say

LessThanPredicate lessThanPredicate = new
LessThanPredicate(equalPredicate);

or do a cast to get it to work. That is nice! Again, this saves people
like myself time . . . lots of time.
Using a general delegate type but a specific parameter name, there's
less room for contradiction. The type indicates what it is (e.g. a
function taking a Person and returning an integer) and the parameter
name indicates what it's used for (e.g. finding the person's age in
years). Why duplicate the information?


If I use a delegate Func<Person,int> then any .NET 3.5 developer worth
their salt will already know what I'm talking about.

They know what a Func<Person, int> looks like, but they don't
necessarily know what it does. Do they look to see what it is or do
they assume. Most developers I know just assume. Better chance of
mistakes that way.
If I introduce a new delegate type, the developer will need to look it
up in order to find out what signature is expected.

How is requiring more work making things *easier* for callers?

Easier means they do things right, not faster or with less brain
power. Easier means less development cost. I prefer spending a year
getting a project out, rather than 3 months to get it out and then 4
years of maintenance. Plus, Intellisense usually handles the
definition just fine. If not, then the parameter documentation. If
not, then the method documentation. If not, then the delegate
definition. See, there is a lot more opportunity for documentation. :-
D
Me too - but that doesn't mean going to unnecessary trouble which
introduces more complexity for no gain, from my perspective.

That is what I am saying. If it makes code easier, I am going to do
it. If not, no way. The question is, do I always have the foresight?
or should I make a simple rule that prevents errors before they
happen? I think, in the majority of cases, using the built-in
delegates makes perfect sense. However, when things start getting more
concrete or generalities are getting to complex, when my callers need
to put extra effort to distinguish between less than and equal to,
then I am going to make things annoyingly blatant and potentially more
time consuming.
 
This has become very interesting, not regarding delegates in particular, but
interesting in regard to group (population) dynamics.

Perhaps a posting subject beginning with "Proper use of...", or some
variant, is not wise in an open forum.

This thread has not only answered my original question, but has also
reminded me that programming dialect varies almost as much as language
dialect. Maybe I mean "common use" rather than dialect, and there is
probably a better analogy still.

Everybody in this thread has a "Preferred use of delegates", and everyone
has evidence and (many times, real world) examples to back up their opinion.

I'm not trying to police the group or anything, but I believe it has come to
the point where everyone is right and the same points are being reiterated
over and over.

It is fascinating to read this thread from the beginning.

I would bet that threads titled "What is the right way to...." outlive their
"How do I..." counterparts at least 3 to 1.

Being new to the group, I didn't think about this until now. In the future
I will subject my questions more concretely. :)
 
Func<T, U> is fairly new. It may describe a generic function, but it
still doesn't say what it does.

It's not supposed to. It's supposed to say what it _is_.

The variable name says what it _does_.
It just saves you from misleading your
callers. And yes, the variables should also be better named. Why say a
variable should have good name but a delegate not?

You are obviously using a prejudiced definition of "good name". In my
opinion, "Func" and "Converter" _are_ good names. They tell you what the
type _is_.

You have started with the assumption that "Func" and "Converter" are not
good names, so of course your conclusion will be that they aren't good
names. Your logic is circular.
You really can't compare an integer to a delegate.

Why not? They are both types.
They do completely different things.

Completely? Hardly. They both define a type that describes what a
variable _is_.

They are different types, true. But that's why they have different
names. No big surprise there. "bool" also is a different type, and it
has a different name. Likewise "double". And "Control". And "String".
And "Socket". Etc.

All of these type names describe what the thing _is_, not what it does.
One is much more abstract than the other.

That's not true at all. Integers are used in lots of widely varying
scenarios, and in many of them the use is at least as abstract as a
delegate, if not more so.
Integers usually act as counters, calculation storage and/or indexes,
their use
is widely known and easy to comprehend.

Really? If you store graphical coordinates as an integer, how is someone
supposed to know just looking at the type that those are coordinates? How
do they tell the difference between that and a counter? Or a calculation
accumulator? Or an index? For that matter, how do they tell a counter
from a calculation accumulator? Or a calculation accumulator from an
index?

Answer: they don't. Because the type name tells you what the data _is_,
not what it does. You need a good variable name to tell you what it does.
That is not true for classes, delegates and other complex types and
concepts.

If I tell you I've got a TextBox, can you tell me what it does? No. All
you can tell me is that it's a Control that displays text. You have no
idea whether I'm displaying HTML source, logged text output from a running
operation, an informational or error message from a single operation, etc.

If I tell you I've got a Socket, can you tell me how I'm using that
Socket? No. All you can tell me is that it represents an abstract data
structure that represents an outlet for network i/o. You have no way to
know whether I'm using it to connect to an FTP server, or to subscribe to
a multicast channel, or to download data from an HTTP server, etc.

I've never heard anyone suggest that these very abstract types are in need
of renaming, so that you can tell from the type how they are actually
being used. I have a hard time believing that even you would make such a
suggestion. So I'm at a loss to understand why you would think that a
delegate type requires different rules than every other type in use in the
language and framework.
[...]
Delegates are confusing to most developers.

Most? I disagree. I recognize that _some_ people take some time to fully
realize the potential of the delegate types, but I would say that most
developers I've run into at least have a basic understanding of the
delegate type as representing a method that can be called.
Few people use them for anything.

Au contraire. Anyone using the System.Windows.Forms namespace is almost
certainly using delegates. Delegates are a critical data type for
Forms-based applications, as they are the basis for the events that are
used to hook up practically all GUI behavior in a Forms-based application.

Given that Forms-based applications represent the lions share of .NET
applications, it's absolutely not true that "few people use them for
anything". Every time someone subscribes to an event, they are using a
delegate.

Furthermore, this is another great counter-example to your assertion that
the delegate should be named to represent what the method does. The
EventHandler delegate type name tells you that the method handles an
event, but it doesn't in any way tell you what behavior is actually being
implemented in the handler. The handler for a Click event, for example,
could do anything. It could open a file, or delete a file, or it could
display a new form, or it could close an existing form, or any number of
other possibilities.

But we don't go around declaring new delegate types to represent all of
those possible actions that the handler might do. Instead, we keep a
single delegate type that describes what the method _is_ (it's a handler
of an event), rather than what it does.
Many OOP experts say they shouldn't be used at all!

And just as many say they are great. So?
If you
are going to use them, make it easy for someone who isn't familiar
with them.

Again, you are using a prejudicial definition for what's "easy". You have
decided a priori that your preferred naming scheme for a delegate makes
things easier, when in fact that's the crux of the disagreement. My
opinion is that requiring a new delegate name for each possible
implementation of a particular delegate signature makes things _harder_,
to understand and to code.
Have types and variable names that are intuitive. You
wouldn't argue if we were talking about a class, what makes a delegate
an exception?

What makes you think I wouldn't argue if we were talking about a class? I
feel the same way about class names as I do about delegates. They should
describe what the class _is_, not what it does.

It's true that there's some gray area, in that in English and in other
languages, we often describe a thing by what it does. "Firefighter",
"nurse", "landscaper", "toaster", "video recorder", etc. are all nouns
that have built into them a description of what that person or thing
does. But that doesn't mean that as a general rule, all things must be
named according to what they do. Rather, it's just an example of how the
human languages have some flexibility built into them that allows for easy
creation of a name for what something _is_, based on what it does.
Also remember that people unfamiliar with functional programming are
generally confused passing a Comparison<T> to a Sort algorithm. Since
they don't know how the method is using the delegate, they don't know
what the return value should be. -1, 0, 1 or 0, -1, 1, or -2, 0, 53???

That's a great reason for that person to read the documentation.
Imagine if they didn't even know it was a comparison! Func<T, T,
int> . . . Um . . . what do I do here?

Um, you read the documentation for the Sort() method.

But more to the point, I think it's interesting that you're willing to
stop halfway in the case of the Comparison<T>, rather than following your
argument to its natural conclusion. After all, according to your
assertion that the delegate type name must clearly state fully what the
method does, we wouldn't even have the generic Comparison<T> type.
Instead, you'd have a CompareTwoStrings, CompareTwoIntegers,
CompareTwoDoubles, etc.
You can document a delegate
type you create, where you can't do anything but give a name to a
Funct<T, T, int>! You can document it in one place instead of having
to redefine its purpose in every method that uses it.

Did you actually read the names of the delegate types that started this
whole sub-thread? You seem not to have. For your benefit, I'll post them
again here:

delegate void callBackString(string d_string);
delegate void callBackStringArray(string[] d_string);
delegate void callBackInt(int d_int);
delegate void callBackSB(string d_string, bool d_bool);

Do you notice something about that list of names? None of them tell you
anything more about what the method does than the recommended Action<T>
that was suggested. "callBack" is redundant for a delegate, since you
always call delegates. There's no other way to use them. And the
remaining parts of the names simply describe the parameters for the
delegate signature (and in the last example, not even that well).

In what way is "callBackString" providing more information to a person
How can argue with that?

See above.
I'm not sure why you always get so hot blooded anytime someone
disagrees with you. It is a reoccuring thing with you.

Actually, it's not. I haven't gotten "hot blooded", and so if you think I
have, that's simply your own prejudiced perception projecting some false
emotional state onto me.

Maybe you should stop trying to infer so much and just pay attention to
the words. Trust me, when I get hot under the collar, you'll know.
It isn't that
I'm not hearing what you are saying. It is just that my opinion is
different than yours and you haven't given me a reason to think it is
otherwise.

I most certainly have given you such a reason. You may disagree with it,
but it's completely false to say I haven't offered anything.

Pete
 
Perhaps here is our difference. I have no problem introducing a new
type if I believe it will clarify something. If it has the opposite
affect, I won't do it. Perhaps in my world creating new types helps
while in your world it doesn't. Environmental influences it seems.

In my world creating new delegate types just for the sake of it doesn't
help when a parameter name can convey
Unless you are very generic, a delegate name can be misleading. Once
you become completely generic you rely on parameter documentation and
good variable names. In the real world neither of these are common.
Perhaps, in a controlled environment with strick documentation rules,
this can be remedied. In my environment, coworkers slop code together
as fast as humanly possible, often not commenting and using classes,
etc. inappropriately or in unclear ways.

And yet you expect them to come up with good delegate names? If they
can do that, why can't they (more simply) use appropriate parameter
names?
I see naming *some* delegates as a way of getting readable code
without comments. Especially if I write the library with the delegates
in the interface. I am telling them that they WILL use my delegate and
and it is called this and, yes, they do have to go look at what it
should do.

So you've got discipline in this one area, but you can't get them to
use appropriate parameter names?
No better way to stop a rampaging developer than to make
him look how to do it right.

.... but you can't explain how good parameter names help?

Your solution seems more complicated than just fixing the problem of
naming parameters appropriately.
Although in my environment, I am usually
being called to their desk to explain it. That works for me. I prefer
to be annoyed regularly with questions than with bug fixes. They still
find ways to mangle things, but things like this certainly help.

If I am developing the library, I have the responsibility to make
delegate names appropriate. It is a design flaw if users of my library
have to work hard to understand my code. A class should be easy to use
correctly and hard to use incorrectly. If I put a bad delegate name
out there, that is my problem. A great way of preventing misleading
delegate names is to create specific ones that say what they should be
doing. I'm not going to reuse Converter<T, U> if it doesn't make
sense. Func<T, U> makes less sense when you look at a concrete class.
You don't throw an Exception, you throw a MySpecialException. True, in
the case of delegates it all turns into the same thing at the end, but
for me it is a chance to take some control.

Exceptions are different, because the type is used elsewhere - to catch
things, and to provide exception-specific information.
Part of my job is fighting bugs, and preventing them is probably my
biggest task. It is like a rookie cyber security officer - they just
want to shut everything off because there is always some risk factor
involved. As they get more experienced with the needs of the users,
they loosen up and learn to mitigate small risks and find a balance
between paranoia and relaxation. We get hit with a lot of bugs (not
always the developer's fault). Since I am trained to mitigate risks, I
tend to be a little more rigid than most developers. But, I have
pumped out many successful libraries that are intuitive and (so far)
bug free. It has already shown improvements in our workflow and it is
all the more reason for me to keep being as rigid. I have a bad habit
of reading books about good programming practice and appling them
vigorously to everything I do. I think very abstractly, so I tend to
bend everything to the same concepts. To me, delegate names fall under
the same lines of good class, variable and method names. There are no
special exceptions.

Do you use "int" though, rather than creating your own Total, Count etc
types? I really don't see the difference.

You seem to keep saying that you have the habit of "doing the right
thing" as if Peter and I prefer shoddy programming. We don't. We just
disagree about what is actually the most maintainable way of
programming.

There nothing I hate more than this:

Customer c = new Customer();

Sure, you know c is a customer, because teh declaration tells you that
is what it is. But, you move down three lines and you are completely
lost again. Instead of c, call it customer. This is also annoying:

C customer = new C();

Sure, you know customer is a customer. You hope customer is a
customer . . . What the heck is a C? You have to look at the
definition of C to see what C is.

Here I'd expect the type to be providing more information - whereas a
delegate type is really *just* a signature.
See? With Func<T, U>, you can't even
look at the definition of Func<T, U> anymore. You have to look at how
it is being used.

You can't see why it's being used without looking at the context (e.g.
the parameter name) - but at that point it's easy.

Compare that to a new, named delegate - at that point you *really*
don't know what it is (you don't know the signature) until you look it
up. You may have a vague idea of what it's meant to achieve, but you
won't know the signature until you check it.

Why do you want to impose that extra lookup on your developers?
Have you ever tried to understand an generic
algorithm without writing it? It is hard! For instance, a big
discussion we had a few weeks ago over one of my libraries came up
after a refactoring took place.

Initially, I had created a delegate that looked like this:

delegate V Func<T, U, V>(T lhs, U rhs);

I would write method like int BinarySearch(IList<T> list, Func<T, T,
int> comparison) and give it to my coworkers.

Well, this is the same as Comparison<T>. However, the specific name is
a *lot* clearer. It doesn't matter what I called that variable, it
still confused my coworkers. They had no where to go to except to me.
That is BAD. I could be hit by a bus at any minute! When I replaced it
with Comparison<T>, the questions stopped coming. They had the
Conversion<T> delegate to look at for documentation.

In some cases, I'd agree - it makes sense. When you're going to use the
same delegate type in potentially many places, because it really is a
general purpose delegate (heck, it's generic here) it makes sense to
encapsulate that.

In particular, here you're going from a generic delegate with three
type parameters to one that only has *one* generic type parameter,
which makes things clearer in itself.

What you've been suggesting is really at the opposite end of the
spectrum though, creating a new delegate type every time you need to
use a delegate. Look at the examples in the original post - these
aren't easily reusable delegate types which will be useful all over the
place. They're there for a very specific purpose, namely calling back
into the UI thread.

Ditto your suggestions of a delegate type to extract the purchase total
for a customer - it's a very specific case, which is better handled
(IMO) with a general delegate type and a very specific parameter name
(and documentation).
Another coworker of mine originally had about 40 methods that took
Predicate<T, T> which he defined himself. It returned whether the left
hand side was less than the right hand side. He was used to how C++
did comparisons. We talked and he modified his methods to use
Comparison<T>. However, he didn't change the variable name from
lessThanPredicate. So all these Comparison were named
lessThanPredicate.

Even worse, he had other Predicate<T, T> that expected true if they
were equal! He called all those equalToPredicate. But, being my fault,
he had changed all those to Comparison<T> also. He was so used to
replacing lessThanPredicate(lhs, rhs) to lessThanPredicate(lhs, rhs) <
0 that when he got to the equality ones, he total spaced and did the
same. None of his code was working. We had to go through all 40
methods to figure out, after the fact, which ones where meant to be
equals and which were meant to be comparison. After all that I told
him to create a new delegate called bool EqualityComparison<T>(T lhs,
T rhs). That kicked it in the butt altogether.

Again, general purpose delegate types - fine.

They know what a Func<Person, int> looks like, but they don't
necessarily know what it does. Do they look to see what it is or do
they assume. Most developers I know just assume. Better chance of
mistakes that way.

Whereas if they've got a name, they have to look up the signature and
then still make assumptions.


Easier means they do things right, not faster or with less brain
power.

To me, it means "easier to do things right with less brain power".
Easier means less development cost. I prefer spending a year
getting a project out, rather than 3 months to get it out and then 4
years of maintenance.

Yes, and no-one has claimed they prefer otherwise.
Plus, Intellisense usually handles the
definition just fine. If not, then the parameter documentation. If
not, then the method documentation. If not, then the delegate
definition. See, there is a lot more opportunity for documentation. :-
D

Yes - and by introducing another delegate type you've introduced
another place where you should have documentation. But earlier on you
were saying that documentation was usually absent! So why not leave
fewer places to be document, but concentrate on getting the
documentation right for those places?
That is what I am saying. If it makes code easier, I am going to do
it. If not, no way.

Except that I believe that by insisting on always creating new delegate
types, you aren't making coding easier. You are in the very general
cases outlined above, but not in the case that the OP was giving, or
your customer age/purchase order total examples.
The question is, do I always have the foresight?
or should I make a simple rule that prevents errors before they
happen? I think, in the majority of cases, using the built-in
delegates makes perfect sense.

That goes directly against your original post to this thread:

<quote>
However, especially when doing UI threading delegates, make as many
delegates as you need so that delegate names make sense.
However, when things start getting more
concrete or generalities are getting to complex, when my callers need
to put extra effort to distinguish between less than and equal to,
then I am going to make things annoyingly blatant and potentially more
time consuming.

And that's all fine - but it's certainly not what you've been appearing
to advocate in other posts. Before this post, your arguments were
mostly based on "people can't write proper parameter names" and you
were applying that argument universally, as far as I could see. The
examples of using a Comparison delegate when that's what you're trying
to do is fine (particularly because the Comparison<T> type is built
into the framework, and thus likely to be familiar to developers).
Applying your ideas to the original posts makes no sense at all, IMO.
 
[...]
Initially, I had created a delegate that looked like this:

delegate V Func<T, U, V>(T lhs, U rhs);

I would write method like int BinarySearch(IList<T> list, Func<T, T,
int> comparison) and give it to my coworkers.

Well, this is the same as Comparison<T>. However, the specific name is
a *lot* clearer. It doesn't matter what I called that variable, it
still confused my coworkers.

You need better co-workers. Seriously.

You named the method "BinarySearch" and you've got a parameter called
"comparison". I don't care what the types are, anyone who knows anything
about basic algorithms should recognize that pattern and understand that
the parameter "comparison" is going to perform the comparison used in the
binary search.

Now, why your binary search method doesn't take a value to search for, and
why you aren't just using a List<T> and calling its BinarySearch, I'm not
sure. But regardless, I don't see anything confusing at all about the
syntax you started out with, and I definitely don't see why changing the
name of the type should make a dramatic improvement in people being able
to understand the method.
They had no where to go to except to me.
That is BAD. I could be hit by a bus at any minute! When I replaced it
with Comparison<T>, the questions stopped coming. They had the
Conversion<T> delegate to look at for documentation.

Why don't they already know what a binary search is? Why aren't they
already familiar with the well-established convention of writing
comparisons that return -1, 0, and 1? First-year undergrad students, and
even high-school students, run into this the first time they have to use
strcmp() or similar functions. Are we really turning out computer
programmers these days that have so little basic background that being
presented with something like this just blocks them until they can come
talk to you to find out what's going on?

I think that's a pretty sad state of affairs, if true.

I will also point out that while MSDN documents a Comparison<T> delegate
as doing a particular thing, it doesn't enforce it. The method really
still is just a Func<T, T, int>. I'm not saying that makes Comparison<T>
a bad choice, but I do question its value in terms of self-documenting
code. If your co-workers aren't with-it enough to recognize a binary
search with comparison delegate when they see one, I'm not convinced you
can rely on them to know that it's up to them to ensure that their
Comparison<T> method does what MSDN says it's supposed to.

Now, all that said...

Please note that I am not saying there's no room for more-qualified type
names. This whole discussion started with an example of some very
general-looking delegate names, and suggestions for using generic delegate
types that would provide an equivalent pattern without requiring the
declaration of a new delegate type, especially one that doesn't follow the
normal .NET conventions anyway.

It seems that we do differ with respect to at what point we'd consider
declaring a new delegate type, but I am not fundamentally against
more-qualified type names such as Comparison<T>. That one makes a lot of
sense, as it's used over and over again in a wide variety of contexts. It
has a definite, well-defined pattern that is repeatedly used in .NET, and
it's a great candidate for being its own delegate type, rather than
relying on the more general-purpose Func<T, T, int> type.

Personally, I think its value is as much in the fact that it incorporates
three different attributes in a single type parameter as anything else
(reduces the changes of accidently putting the wrong type as one of the
type parameters). But if you find it more useful to have that specific
name rather than "Func" as well, this is a situation in which declaring a
special-purpose delegate type can be leveraged over a fairly wide variety
of code.

Likewise, if you've got a situation where you expect to be using the same
exact delegate type over and over, and that delegate type can be named in
a way that is very specific to its use rather than just redescribing the
signature of the method, a custom delegate type, generic or otherwise,
could well be warranted.

But my opinion is that for one-off uses, or where you're declaring a
delegate type that is only specific to the type and number of method
parameters anyway, the predefined Action<> and Func<> delegate types are
perfectly fine and just as useful as some custom-defined delegate type.
They have the advantage that they are (or should be) well-known and easily
recognized by someone writing .NET code. Using them also avoids
cluttering your namespace with a variety of well-intentioned but redundant
delegate names.
[...]
Here creating a better name would have prevented the error altogether.
Variable names were misleading because they weren't changed with their
types. In the beginning all we had was Func<T, T, bool> and look how
we managed to turn it into a Comparison<T> and a
EqualityComparison<T>. Is that convincing enough? It is for me!

I'm not sure what you mean here. You said he started with his own
custom-defined "Predicate<T, T>". At what point did you have a "Func<T,
T, bool>" and in what way was that making the issue confusing?

I also don't see how changing from a method signature that simply returns
a boolean according to whether the lhs is less than the rhs, to one that
returns a value that is less than 0, 0, or greater than 0 improves the
readability of the code. Presuambly if the boolean return value was
sufficient before, the code was designed such that a simple if() using the
return value worked fine. With a Comparison<T>, now you have to compare
the return value to 0. By your own statement, your coworkers are
apparently not accustomed to the idea of a comparison result being
comparable to 0, so it seems like this would just make things more
confusing.

I also am a bit suspicious of reusing the "Comparison" name in the
"EqualityComparison", given that _that_ delegate is in fact going to
return a boolean rather than the integer that is part of the convention
for Comparison<T>. This is just the sort of naming overlap that can lead
to confusion.

If you're already taking an essentially boolean operation and using the
Comparison<T> delegate type anyway, why not just always use
Comparison<T>? When you need the "less than" behavior, you compare the
result as "< 0", and when you need the "equal to" behavior, you compare
the result as "== 0". Furthermore, if you follow that convention, then
you can write true comparison methods, and use the exact same method
whether you're doing the "less than" or the "equal to" check.

In other words, while I admire the goal to make the code less confusing,
it doesn't really seem to me that you've done so with the addition of a
new delegate type, EqualityComparison<T>. Instead, you'd added a new
scenario for potential confusion while at the same time forced yourself to
write essentially the same logic twice (once for a Comparison<T> method
and once for an EqualityComparison said:
The same guy told me that while he was developing, he changed the name
from Predicate to BinaryPredicate and back again. He had an overloaded
predicate that took two different types. He had a lot of Predicates! I
think more confusing than anything is seeing Predicate<T, T> being
used for less than in 20 methods and equal to in the other 20.

I guess that's an eye-of-the-beholder thing then. I think that
"Predicate" makes it very clear that you're going to take two operands and
return a true/false value. That's what a predicate does.

The type doesn't tell you what the operation is actually going to be, but
even by the description you provided it seems clear that the variable
names did make it clear.

Of course, given the overall problem description, I would (as I mentioned
above) likely go with a straight comparison method, using Comparison<T>
everywhere. But inasmuch as you might have a method that returns a
boolean value based on the inputs, the "predicate" nomenclature is
well-understood and consistent.
I wouldn't know whether to trust the delegate name or the parameter
name.

Huh? You can always trust the delegate name, as it's a type and so must
tell you exactly what the variable _is_. If the type is wrong, the code
isn't going to work.

The parameter name is potentially unreliable, but that's a development
practices issue if the variable doesn't give you a clear indication as to
what it's being used for. You _should_ be able to trust the parameter
name, and even if you have a type that always matches the variable usage
precisely, it's a serious problem to have a parameter name that does not
do so.
Furthermore, since the delegates are the same, you could pass an
"equal to" delegate to a "less than" delegate and everything will
compile. Using specific names forces a compile time check. You
actually have to say

LessThanPredicate lessThanPredicate = new
LessThanPredicate(equalPredicate);

or do a cast to get it to work. That is nice! Again, this saves people
like myself time . . . lots of time.

Actually, a cast won't work. You have to instantiate a new delegate. But
regardless, this is a potential boon or a potential hindrance, depending
on the situation. It's not a general purpose argument against using the
more general-purpose delegates or for always naming a delegate type as
specifically as you can, because only in some cases is it detrimental to
be able to assign predicate variables to each other.

There is also the problem that as long as the signature of a method
matches, you can create any delegate that has the same signature. So,
while what you're talking about above does prevent assignment of one
delegate type to another, it doesn't do anything to stop you from using a
method written for a LessThanPredicate where you expected an
EqualPredicate:

delegate bool LessThanPredicate<T>(T t1, T t2);
delegate bool EqualPredictate<T>(T t1, T t2);

bool FEqualMethod<T>(T t1, T t2)
{
return t1.Equals(t2);
}

void SomeOtherMethod()
{
LessThanPredicate<int> ltp = FEqualMethod<int>; // OOPS!
}

In other words, the point of confusion hasn't been eliminated. It's just
been shifted to a different location in the code.
[...]
If I use a delegate Func<Person,int> then any .NET 3.5 developer worth
their salt will already know what I'm talking about.

They know what a Func<Person, int> looks like, but they don't
necessarily know what it does.

IMHO, I think we've already established that Converter<Person, int> is a
poor choice for the scenario in which this came up. If you were really
mapping a person to an integer in a way in which that integer represented
the original person, it'd be the right delegate to use. But in the given
example, you're simply writing a function that takes as input a person,
and returns some arbtirary integer that is not tied to that specific
person at all.

So, at least in that example, the Func<Person, int> delegate is actually
much less misleading than the Converter<Person, int>. Someone familiar
with .NET reading code that uses Converter<Person, int> is going to assume
that the delegate method is returning an integer that is simply an
alternative representation of that person. Because that's the sort of
thing that Converter said:
Do they look to see what it is or do
they assume. Most developers I know just assume. Better chance of
mistakes that way.

Developers shouldn't assume. But they do, you're right. Which is why
Func<Person, int> is a superior choice to Converter<Person, int> in this
case.

Pete
 
I'd just like to assert that, contrary to the evidence in this thread,
Peter and I really are different people :)

You /claim/ that, but I've never seen you both in the same room!

Of course, I've never seen either of you... ;-p

Marc
 
This has become very interesting, not regarding delegates in particular,
but interesting in regard to group (population) dynamics.

Perhaps a posting subject beginning with "Proper use of...", or some
variant, is not wise in an open forum.

I think you overestimate the power of the subject line. :)
[...]
I'm not trying to police the group or anything, but I believe it has
come to the point where everyone is right and the same points are being
reiterated over and over.

Well, I think there are subtle differences in each reply. I haven't seen
anyone repeat themselves (though I admit that Jon and I may have repeated
each other a little bit).

It seems to me that the biggest disconnect here is that
"(e-mail address removed)" seems to be assuming that I (and perhaps Jon)
believe that there's no reason to ever create a new delegate type, given
the existence of the generic Action and Func types. Nothing could be
farther from the truth. Those types cover every possible signature up to
four parameters, which means they _could_ be applied to practically any
reasonable method. But I would never suggest that they should _always_ be
used for all delegate types.

Instead, my point is that sometimes you really don't have a compelling
need for declaring a whole new delegate type for a once-in-awhile delegate
instance. In those cases, the Action and Func delegate types are
appropriate and very useful.

If you've got a specific kind of delegate that you anticipate reusing on a
regular basis, by all means: declare a new delegate type to represent that
delegate.

As you might note from some of the replies, I think some of his examples
are flawed. And I do believe that any example intended to prove that one
should _never_ use Action or Func is congenitally flawed. But that's not
to say that one couldn't come up with plenty of examples where something
other than Action or Func is a better choice. .NET is filled with such
examples, the use of delegates being so pervasive in the framework and
Action and Func being used so infrequently within the framework. It'd be
pretty remarkable if the .NET designers had gotten it wrong so many
times. :)
It is fascinating to read this thread from the beginning.

I would bet that threads titled "What is the right way to...." outlive
their "How do I..." counterparts at least 3 to 1.

If so, it would be because of the content of the original post and ensuing
discussion. If your question is really "what is the right way to...",
then it's not true that naming your subject "how do I..." is going to
change the direction the discussion heads.
Being new to the group, I didn't think about this until now. In the
future I will subject my questions more concretely. :)

See above. You should "subject your questions" according to the actual
question. This will provide clarity and a frame of reference for those
responding to your question.

More significantly, I don't think you should spend much time worrying
about how other people will respond to each other based on your own
question and/or subject description for the question. That sort of
self-consciousness is only going to get in the way of you writing
effective questions.

When you're writing a question, just focus on clearly stating what it is
you want to know. Don't waste much time, if any, worrying about what
topics might be controversial or whether you might be able to direct the
discussion more precisely with some well-chosen words in the subject
line. Doing so assumes you have way more control over how people respond
than you actually do. :)

Pete
 
Func said:
still doesn't say what it does.

Delegate types *never* say what they do; just the signature. I've seen
various abuses of this "because the signature matched", but perhaps
the most well-known example is this (very common in 2.0 console/
service code; obviously winform has MethodInvoker that does the
same...)

// nothing to do with threading - just a void Foo() callback (etc)
ThreadStart bar = SomeMethod;

The best way to express the *intent* (i.e. how it should behave) of a
delegate argument is in the arg-name and the supporting xml (///)
comments.

As for delegates being confusing and/or not used much: I expect this
will change *significantly* in the short-term as LINQ (and associated
techs) kicks off (don't forget: the /official/ launch isn't until next
month!); C# 3 makes it much easier to write functional-style code,
and from experience I know it is a *huge* win, especially when
combined with lambdas and the crossover to Expressions.

Marc
 
Peter Duniho said:
[...]
When you're writing a question, just focus on clearly stating what it is
you want to know. Don't waste much time, if any, worrying about what
topics might be controversial or whether you might be able to direct the
discussion more precisely with some well-chosen words in the subject
line. Doing so assumes you have way more control over how people respond
than you actually do. :)

Pete


Probably more than anything I just wanted to post _something_, albeit
useless observations, since this has left the scope of my
understanding...days ago. :)

(doh! I'm doing it again)

But on a positive note, I received Mr. Troelsen's book in the mail today
(err, yesterday) so now I have a quicker way (compared to first figuring out
which Google result is relevant) to look up the terms that I don’t yet
understand.

....I also finally figured out how to view threads by topic in Windows Live
Mail, that’s handy.
 
Peter said:
Yes. (Apparently) the compiler will do it for you as long what you're
passing doesn't look like an object[] to the compiler (as it did when
before you were casting). But any time you don't need to pass an array
of parameters, yes...you could explicitly do the cast just to make sure
that the overload you want is the one that's actually used.

I wrote "apparently", because I wasn't even aware that you could do
this. Control.Invoke() only has two overloads, neither of which allow a
simple object instance as the second parameter. I assume that there's
some compiler "funny business" going on that allows a simple object
instance to be automatically converted to the required object[] needed
by Invoke(). But I don't know what that is.

If my understanding is correct, there's no funny business. The second parameter
is a *params* object[], so it could be 1 to N arguments. All you're doing is
converting the string[] you have to an object so that the compiler doesn't think
you're passing in the complete array of arguments, and instead treats it as just
one parameter.


Chris.
 
Peter Duniho wrote:
[...]
You need better co-workers. Seriously.

I had to laugh when I read this -- I've felt the OP's pain on more than one
occasion at both my current place of employ and previous ones.
This is probably the appropriate (well, *accurate*) response to probably two
thirds of the lengthy discussions that take place here that mention work
environment in any way.

Chris.
 
If my understanding is correct, there's no funny business. The second
parameter is a *params* object[]

Ah, right. It's funny...I was actually looking for that "params" keyword
in the docs, and didn't see it. When I went back and looked at the exact
same page I'd looked at before, sure enough there it was.

I think someone is messing with my head. :)

Pete
 
[...]
Initially, I had created a delegate that looked like this:
delegate V Func<T, U, V>(T lhs, U rhs);
I would write method like int BinarySearch(IList<T> list, Func<T, T,
int> comparison) and give it to my coworkers.
Well, this is the same as Comparison<T>. However, the specific name is
a *lot* clearer. It doesn't matter what I called that variable, it
still confused my coworkers.

You need better co-workers.  Seriously.

Not every programmer is trained to be one. They are great coworkers.
They just lack training. Some of them are dead weight, but others are
amazingly good and are only getting better.
You named the method "BinarySearch" and you've got a parameter called  
"comparison".  I don't care what the types are, anyone who knows anything  
about basic algorithms should recognize that pattern and understand that  
the parameter "comparison" is going to perform the comparison used in the  
binary search.

What is a comparison? Greater than? Less than? Equal to? Greater Equal
To? All of the above? And true, any trained programmer knows all about
binary search. Any C programmer knows that comparison returns -1, 0,
1. Anyone with Intellisense and intelligence will eventually figure
out what to do. Most of the algorithms from C# and even C++ have
inherent delegate types. How many of use went from writing a for loop
to a ForEach loop overnight? How many of us use algorithms as much as
we should? How many of us use it when we shouldn't? Is that relavent?

I wish I worked in a big shop with some of the best developers in the
world, but I'm fairly new to the professional world. I get to show
what I know, if anything, by arguing with you "big shots". Outside of
that, I get to work with humble non-programmers who have learned more
in a year's time than most Stanford students learn before they
graduate. It is exciting and a bit of a let down. Is that relevant
either?

Truly, any C# developer should eventually know what Comparison is.
When they get to see all they places they get to use it, woo hoo!
Now, why your binary search method doesn't take a value to search for, and 
why you aren't just using a List<T> and calling its BinarySearch, I'm not  
sure.  But regardless, I don't see anything confusing at all about the  
syntax you started out with, and I definitely don't see why changing the  
name of the type should make a dramatic improvement in people being able  
to understand the method.

My examples are never very real world. Perhaps they should be. Imagine
you didn't know anything about programming. Imagine that you knew
everything BUT binary search and comparison. You had to pick this up
and work with it. No one is around, you're stuck in a black box of
misery and dispair. The only way out is to figure out how to find the
number 3154 in a list of 10 bagillion numbers. You have no
documentation and with every second ticking by you grow a little more
insane and a little more facial hair.

With impending doom encompassing you, do you grab for a method called
Comparison, or do you grab for Func?

Okay, maybe a bit dramatic. Maybe from the wrong angle. Now, believe
me, I like Func as much as the next guy. But in my world, using it is
asking for problems. I avoid them. I go for specific names. Even if
they aren't very well-named. Even if they documentation stinks. Maybe
I'm a looney! But, whatever the reason, the people here make less
mistakes when I stop making a bunch of Func<int, bool> that have 20
different meanings and have 20 different delegates. I'm not arguing
with you, but I'm *definitely* not arguing with reality.
Why don't they already know what a binary search is?  Why aren't they  
already familiar with the well-established convention of writing  
comparisons that return -1, 0, and 1?  First-year undergrad students, and  
even high-school students, run into this the first time they have to use  
strcmp() or similar functions.  Are we really turning out computer  
programmers these days that have so little basic background that being  
presented with something like this just blocks them until they can come  
talk to you to find out what's going on?

I'm not talking specifically about BinarySearch (they're not that bad
off). I'm not talking about a crack team of developers either.
Honestly, if you want me to work with people who know what
BinarySearch is, get a job with you so we can argue in person. :- )

From my understanding, many programmers today didn't go to school or
start out as CS majors. Many of them just fell into it. They are doing
fine. I wouldn't be surprised if that trend continued, the way
students avoid sciences anymore.
I think that's a pretty sad state of affairs, if true.

Oh, it is. Cry, cry a lot.
I will also point out that while MSDN documents a Comparison<T> delegate  
as doing a particular thing, it doesn't enforce it.  The method really  
still is just a Func<T, T, int>.  I'm not saying that makes Comparison<T>  
a bad choice, but I do question its value in terms of self-documenting  
code.  If your co-workers aren't with-it enough to recognize a binary  
search with comparison delegate when they see one, I'm not convinced you  
can rely on them to know that it's up to them to ensure that their  
Comparison<T> method does what MSDN says it's supposed to.

That is a good point. If MS screws up, it is there problem. If I don't
follow a convention in a library I write, that is my problem. I am not
distributing the code across the globe; it is staying in house. Names
just give me more reason to follow a convention. It gives my coworkers
something new to learn, rather than just another detail to get their
code to work. No one said that C#'s delegate types are the only ones
allowed to be standard . . . in context.
Now, all that said...

Please note that I am not saying there's no room for more-qualified type  
names.  This whole discussion started with an example of some very  
general-looking delegate names, and suggestions for using generic delegate 
types that would provide an equivalent pattern without requiring the  
declaration of a new delegate type, especially one that doesn't follow the 
normal .NET conventions anyway.

I agree. His delegate names didn't convey anything. My suggestion
wouldn't have been to introduce Func and Action everywhere. It would
have been to make even more - more specific delegates. Maybe my code
is a little fatter. I don't care, really.
It seems that we do differ with respect to at what point we'd consider  
declaring a new delegate type, but I am not fundamentally against  
more-qualified type names such as Comparison<T>.  That one makes a lot of  
sense, as it's used over and over again in a wide variety of contexts.  It  
has a definite, well-defined pattern that is repeatedly used in .NET, and  
it's a great candidate for being its own delegate type, rather than  
relying on the more general-purpose Func<T, T, int> type.

I think we are coming to an agreement. You know, we as developers need
to decide when creating a new delegate is making our code easier to
understand and when it is just being obnoxious. When I think about
this, the example that comes to my head is of a form I wrote a while
back. Then, I had delegates working with BeginInvoke to handle
asyncronous events updating a DataGridView. At first I used very
generic names for my event handlers that all took an object and a
EventArgs subclass. They all had the same signiture. However, some of
them were adding rows, others updating columns.

It wasn't so much confusing to keeping track, I just didn't like how I
left myself open to pass an update-column delegate to a add-row
delegate. So, I added a defense. Perhaps it made my code harder to
understand (I don't think so). At any rate, the code worked before.
Did adding a delegate name fix anything. No. Did it make maintenance
easier . . . we'll see.

However, I can see where not being a naming freak can be advantageous.
I'm not going to argue about what you have been saying. You have been
making very valid and thought-out arguments (even if they do reak of
Aristotlean logic).
Personally, I think its value is as much in the fact that it incorporates  
three different attributes in a single type parameter as anything else  
(reduces the changes of accidently putting the wrong type as one of the  
type parameters).  But if you find it more useful to have that specific  
name rather than "Func" as well, this is a situation in which declaring a  
special-purpose delegate type can be leveraged over a fairly wide variety  
of code.

Likewise, if you've got a situation where you expect to be using the same  
exact delegate type over and over, and that delegate type can be named in  
a way that is very specific to its use rather than just redescribing the  
signature of the method, a custom delegate type, generic or otherwise,  
could well be warranted.

That is what I was attempting to say before.
But my opinion is that for one-off uses, or where you're declaring a  
delegate type that is only specific to the type and number of method  
parameters anyway, the predefined Action<> and Func<> delegate types are  
perfectly fine and just as useful as some custom-defined delegate type.  
They have the advantage that they are (or should be) well-known and easily 
recognized by someone writing .NET code.  Using them also avoids  
cluttering your namespace with a variety of well-intentioned but redundant 
delegate names.

I'm not arguing that either. It is a matter of judgement.
[...]
Here creating a better name would have prevented the error altogether.
Variable names were misleading because they weren't changed with their
types. In the beginning all we had was Func<T, T, bool> and look how
we managed to turn it into a Comparison<T> and a
EqualityComparison<T>. Is that convincing enough? It is for me!

I'm not sure what you mean here.  You said he started with his own  
custom-defined "Predicate<T, T>".  At what point did you have a "Func<T, 
T, bool>" and in what way was that making the issue confusing?

He didn't know there was a Comparison, obviously. The idea here was
that he was being generic pushing two delegate types into one. One was
for less than, the other for equality.

He didn't realize how confusing that could be. Not because the use
couldn't be inferred, but because people don't always look when they
refactor. Furthermore, I told him Comparison was a well-known
standard. When he went to refactor, he unintentionally changed them
all to be comparisons, regardless whether they were for less than or
equal to. That kind of change can't go unchecked without something
breaking.

In our case, being really generic didn't help. I hope I made that
clear. Yes, even the parameter names can't save you when your library
provider suddenly decides to change an equal to to a less than. That
was his and my fault.

My point was that had he initially given better names, he might have
avoided the issue. He should have been more careful refactoring, as
well. But in his case, perhaps unspecific delegates hindered his
refactoring efforts.

Take it as you will.
I also don't see how changing from a method signature that simply returns  
a boolean according to whether the lhs is less than the rhs, to one that  
returns a value that is less than 0, 0, or greater than 0 improves the  
readability of the code.  Presuambly if the boolean return value was  
sufficient before, the code was designed such that a simple if() using the 
return value worked fine.  With a Comparison<T>, now you have to compare 
the return value to 0.  By your own statement, your coworkers are  
apparently not accustomed to the idea of a comparison result being  
comparable to 0, so it seems like this would just make things more  
confusing.

Comparison has documentation. They only have to learn it once. Better
standard than nothing.
I also am a bit suspicious of reusing the "Comparison" name in the  
"EqualityComparison", given that _that_ delegate is in fact going to  
return a boolean rather than the integer that is part of the convention  
for Comparison<T>.  This is just the sort of naming overlap that can lead  
to confusion.

We argued about that one for a while too. Comparison doesn't work with
equal to.
If you're already taking an essentially boolean operation and using the  
Comparison<T> delegate type anyway, why not just always use  
Comparison<T>?  When you need the "less than" behavior, you compare the  
result as "< 0", and when you need the "equal to" behavior, you compare  
the result as "== 0".  Furthermore, if you follow that convention, then  
you can write true comparison methods, and use the exact same method  
whether you're doing the "less than" or the "equal to" check.

Some types can be tested for equivalence, but not for ordinal
position. You should know that.
In other words, while I admire the goal to make the code less confusing,  
it doesn't really seem to me that you've done so with the addition of a  
new delegate type, EqualityComparison<T>.  Instead, you'd added a new  
scenario for potential confusion while at the same time forced yourself to 
write essentially the same logic twice (once for a Comparison<T> method  
and once for an EqualityComparison<T> method).

Again, you can't always find a ordinal position where you can find an
equivalence. It is more confusing to me to say, "Well, return anything
you want, so long as it's not zero," to indicate false.
I guess that's an eye-of-the-beholder thing then.  I think that  
"Predicate" makes it very clear that you're going to take two operands and 
return a true/false value.  That's what a predicate does.

It can take one argument. It could technically take N. TrueForAll
could be an N predicate.

The problems not in the use. It's in the readibility. I honestly would
prefer a lessThan delegate. I know it seems to defeat the point. But,
when there is a strong mix of predicates in a library, what makes more
sense? If you're working with experienced developers, it's obvious. We
know how the predicate's being used. For people who are unfamiliar, it
is a guessing game. There are a lot more methods out there taking
delegates than C# provides by default. Additionally, know matter how
less than is represented, it is still less than. You are abstracting
how less than is determined, not whether it is less than. Less than
could be greater than. In that case, perhaps it should be called
OridinalSpecifier and EqualitySpecifier.
The type doesn't tell you what the operation is actually going to be, but  
even by the description you provided it seems clear that the variable  
names did make it clear.

Again, why one and not the other. If it makes sense, do it.
Of course, given the overall problem description, I would (as I mentioned  
above) likely go with a straight comparison method, using Comparison<T>  
everywhere.  But inasmuch as you might have a method that returns a  
boolean value based on the inputs, the "predicate" nomenclature is  
well-understood and consistent.


Huh?  You can always trust the delegate name, as it's a type and so must 
tell you exactly what the variable _is_.  If the type is wrong, the code 
isn't going to work.

Predicate said:
The parameter name is potentially unreliable, but that's a development  
practices issue if the variable doesn't give you a clear indication as to  
what it's being used for.  You _should_ be able to trust the parameter  
name, and even if you have a type that always matches the variable usage  
precisely, it's a serious problem to have a parameter name that does not  
do so.

Many people still name things like c, a, t, q . . . They obvious
stand for counter, ascii, tens and quicksort. You have been living
developer heaven for too long.
Actually, a cast won't work.  You have to instantiate a new delegate.  But  
regardless, this is a potential boon or a potential hindrance, depending  
on the situation.  It's not a general purpose argument against using the 
more general-purpose delegates or for always naming a delegate type as  
specifically as you can, because only in some cases is it detrimental to  
be able to assign predicate variables to each other.

I know it can't work. All the better. No point passing a
OrdinalSpecifier as a EqualitySpecifier. It did happen, so I will
follow my way.
 
Many people still name things like c, a, t, q . . . They obvious
stand for counter, ascii, tens and quicksort. You have been living
developer heaven for too long.

This is the same argument you've made several times though: "People
can't be trusted to write decent parameter names."

As far as I've seen, you haven't answered the counter-argument: "Isn't
it easier to ask them to write a good parameter name (changing a single
identifier) than to come up with a new delegate type, name that
appropriately, *and* name the parameter appropriately?"

How is it easier to find proper names for both a delegate type and a
parameter than just a parameter?
 

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