Tough Sentence about Generic Overloading


J

James A. Fortune

I've finished reading the two "Effective C#" books. The "null
coalescing operator," i.e., '??' was pretty interesting. I have a
question (eventually). In "More Effective C#," p. 130 [Item 21:
Create Method Groups That Are Clear, Minimal, and Complete]:

[Sample Code with overloads for short, int, float and double]

"If you have a C++ background, you probably wonder why I haven't
recommended replacing all those overloads with a single generic
method. That's because C# generics don't support that practice in the
way C++ templates do. With C# generics, you can't assume that
arbitrary methods or operators are present in the type parameters.
You must specify your expectations using constrains. Of course, you
might think about using delegates to define a method constraint. But
in this case, that technique only moves the problem to another
location in the code where both the type parameter and the delegate
are specified. You're stuck with some version of this code.

However, suppose you left out some of the overloads:

public void Scale(float scaleFactor)
{
for (int index = 0; index < values.Count; index++)
values[index] *= scaleFactor;
}

public void Scale(double scaleFactor)
{
for (int index = 0; index < values.Count; index++)
values[index] *= scaleFactor;
}

Now it's a bit trickier for users of the class to determine which
method will be called for the short and the double cases. There are
implicit conversions from short to float, and from short to double.
Which one will the compiler pick? And if it can't pick one method,
you've forced coders to specify an explicit cast to get their code to
compile. Here, the compiler decides that float is a better match than
double. However, most of your users may not come to the same
conclusion. Here's how to avoid this problem: When you create
multiple overloads for a method, make sure that most developers would
immediately recognize which method the compiler will pick as a best
match. That's best achieved by providing a complete set of method
overloads."

Later, he goes on to say:

"Adding a generic method to catch all the missing cases, using a
default implementation, creates an even more sinister situation:

public static class Utilities
{
// Prefer Math.Max for double:
public static double Max(double left, double right)
{
return Math.Max(left, right);
}

// Note that float, int, etc. are handled here:
public static T Max<T>(T left, T right)
where T : IComparable<T>
{
return (left.CompareTo(right) > 0 ? left : right);
}
}
double a1 = Utilities.Max(1,3 );
double a2 = Utilities.Max(5.3, 12.7f);
double a3 = Utilities.Max(5, 12.7f);

....

Yes, even if there are obvious candidate methods that require implicit
conversions, the generic method is considered a better method match
whenever it is accessible.

But I'm not finished throwing complications at you. Extension methods
can also be considered in the mix. What happens if an extension
method appears to be a better match than does an accessible member
function? Well, a better match means a method that has a better-
matching parameter list. If the extension method is a better match --
meaning that none of the parameters needs any conversions -- then the
compiler prefers the extension method to an accessible member function
that requires conversions. Given that generic methods always create a
better match, you can see why extension methods can wreak havoc with
method lookup."

I liked the perspective of coding such that others can enhance your
code effectively and wisely. That wasn't the first time he gave an
example, and then noted somehow that there was a problem lurking.
Then he fixed that problem and noted again that the newly fixed
problem had a new problem lurking. It didn't help my
confidence :). I had trouble fully comprehending the first quoted
paragraph above. Can someone enlighten me?

Optional: How good are VS' tools at detecting lurking "gotchas"?

I expect to have more questions about the content I read in those
books. The next C# books I plan to read are:

Pro C# 2010 and the .NET 4 Platform
Apress, 2010
Andrew Troelsen
ISBN 978-1-4302-2549-2

and

Professional C# 4 and .NET 4
Wiley, 2010
Nagel, Evjen, Glynn, Watson, Skinner
ISBN 978-0-470-50225-9

Optional: I would be interested in hearing any general comments about
either of those books.

Thanks in advance,

James A. Fortune
(e-mail address removed)

GW: I'll tell you anything you want to know about women.
JF: O.K. What's the biggest secret that women have?
GW: That sex is a great cure for a woman's headache.
 
Ad

Advertisements

A

Arne Vajhøj

I've finished reading the two "Effective C#" books. The "null
coalescing operator," i.e., '??' was pretty interesting. I have a
question (eventually). In "More Effective C#," p. 130 [Item 21:
Create Method Groups That Are Clear, Minimal, and Complete]:

[Sample Code with overloads for short, int, float and double]

"If you have a C++ background, you probably wonder why I haven't
recommended replacing all those overloads with a single generic
method. That's because C# generics don't support that practice in the
way C++ templates do. With C# generics, you can't assume that
arbitrary methods or operators are present in the type parameters.
You must specify your expectations using constrains. Of course, you
might think about using delegates to define a method constraint. But
in this case, that technique only moves the problem to another
location in the code where both the type parameter and the delegate
are specified.
I had trouble fully comprehending the first quoted
paragraph above. Can someone enlighten me?

With C++ templates you can write:

template<class T> T Max(T a, T b)
{
return (a > b) ? a : b;
}

with C# generic you can write:

public static T Max<T>(T a, T b) where T : IComparable<T>
{
return (a.CompareTo(b) > 0) ? a : b;
}

Besides the small syntactical differences there is a
major difference in how C++ templates and C# generics
works.

In C++ you just do:
(a > b)
and the compiler will give an error if > is not defined for T.

In C# you have to do:
where T : IComparable<T>
to tell that T implements the interface IComparable<T> so
you can then do:
(a.CompareTo(b) > 0)

Big difference.

Arne
 
Ad

Advertisements

J

James A. Fortune

I've finished reading the two "Effective C#" books.  The "null
coalescing operator," i.e., '??' was pretty interesting.  I have a
question (eventually).  In "More Effective C#," p. 130 [Item 21:
Create Method Groups That Are Clear, Minimal, and Complete]:
[Sample Code with overloads for short, int, float and double]
"If you have a C++ background, you probably wonder why I haven't
recommended replacing all those overloads with a single generic
method.  That's because C# generics don't support that practice in the
way C++ templates do.  With C# generics, you can't assume that
arbitrary methods or operators are present in the type parameters.
You must specify your expectations using constrains.  Of course, you
might think about using delegates to define a method constraint.  But
in this case, that technique only moves the problem to another
location in the code where both the type parameter and the delegate
are specified.
                       I had trouble fully comprehending the first quoted
paragraph above.  Can someone enlighten me?

With C++ templates you can write:

template<class T> T Max(T a, T b)
{
     return (a > b) ? a : b;

}

with C# generic you can write:

public static T Max<T>(T a, T b) where T : IComparable<T>
{
    return (a.CompareTo(b) > 0) ? a : b;

}

Besides the small syntactical differences there is a
major difference in how C++ templates and C# generics
works.

In C++ you just do:
   (a > b)
and the compiler will give an error if > is not defined for T.

In C# you have to do:
   where T : IComparable<T>
to tell that T implements the interface IComparable<T> so
you can then do:
   (a.CompareTo(b) > 0)

Big difference.

Arne

Thanks for the answers. I'll start compiling and assembling :) the
other questions I have from those two books. Note: I've programmed in
Visual C++ before, but I did not need to get involved with C++
templates for that work. Both explanations were helpful.

James A. Fortune
(e-mail address removed)

When I tried to hold back, I became unintelligible. So I preferred to
be a bit talkative. -- Henri Poincaré
 

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

Top