2
public static T clipGN<T>(this T v, T lo, T hi) where T : decimal,double
{ return Math.Max(lo, Math.Min(hi, v)); }

gives for the second line:

Argument 1: cannot convert from 'T' to 'decimal'

Why? I thought both types meeting that T constraint can be converted to decimal.

BTW, an acceptable alternative coding can be found in the answer here: How should one best recode this example extension method to be generic for all numeric types?

Community
  • 1
  • 1
ChrisJJ
  • 2,191
  • 1
  • 25
  • 38
  • see http://stackoverflow.com/questions/32664/c-generic-constraint-for-only-integers – sehe Aug 20 '11 at 23:22

2 Answers2

1

You don't need generics for this. While the concept of "DRY" makes the idea of coding a single function that can work for all types, this is a case where you're better off having discreet functions for each numeric type. All of the numeric types are known, and the list is not overly large; there are likely numeric types that you aren't actually going to use, anyway. If you really (for whatever reason) want a single function, then your only real option is the IComparable option that you linked to, which has the unfortunate (and unnecessary) consequence of causing boxing on the numeric parameters.

That being said, your problem is that you cannot have T : decimal, double, as that means that T must be both decimal and double (which is impossible), not that it can be either one.

In addition, since this is all that this function does, I'd probably not call the Math.Max and Math.Min functions anyway. It's probably just as simple, if not slightly clearer, to write the functions this way:

public static decimal ClipGN(this decimal v, decimal lo, decimal hi)
{
    return v <= lo ? lo : v >= hi ? hi : v;
}

And you should be able to duplicate this code verbatim (apart from the return and parameter types, of course) for each of the numeric types.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • 1
    Thanks. Can you tell me the advantage of duplicating either your or my type-specfic definition for each numeric types (up to eleven), over using a generic? – ChrisJJ Aug 21 '11 at 00:07
  • @Chris: The advantage is that the generic function can only operate on `IComparable`, which will require the numeric value to be boxed into a reference type. This requires that the value be copied to the heap, then a reference created for it (this is true of all three parameters, as well as the return value). While this isn't an extremely expensive operation in and of itself, it's unnecessary and doesn't actually *get* you anything, other than not having to copy the function. – Adam Robinson Aug 21 '11 at 00:20
  • Thanks. That performance cost sounds to me worth paying to avoid having to copy the function and more importantly having to maintain the copies. One does though wonder why the optimiser doesn't eliminate that cost. – ChrisJJ Aug 21 '11 at 00:31
  • @ChrisJJ: I'm curious what you would need to maintain in a function this simple. Can you reasonably forsee the meaning of this function changing? YAGNI is just as important as DRY. – Adam Robinson Aug 21 '11 at 00:33
  • @ChrisJJ: The optimizer can't eliminate this cost; dealing with a value type as an interface *requires* boxing, as interfaces are reference types. There's no way around that. – Adam Robinson Aug 21 '11 at 00:34
  • 2
    re curious: I can foresee the possibility of change to the definition of any function I write. I don't like having eleven identical copies of a function body complicating changes, breakpointing etc. On "YAGNI is just as important as DRY", I disagree. Re the optimiser, yes I now see it obstructed by the requirement to deal with a value type as an interface. Sad that it cannot handle something that a simple macro can do in other languages. – ChrisJJ Aug 22 '11 at 13:23
  • @ChrisJJ: Generics are not macros or C++ templates, and their purposes shouldn't be confused. – Adam Robinson Aug 22 '11 at 14:03
1

I tried compiling that code myself and I receive the following error myself:

'double' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter. (CS0701)

Same for decimal. This suggests that neither decimal nor double are allowed to constrain the type parameter T since the only types that could meet that constraint are themselves (it would be no different from making a non-generic overload, replacing T with either decimal or double). Even if, individually, they were allowed to constrain T (which they are not), the combination constraint should still not be allowed since no type can simultaneously be a decimal and a double.

This is unlike if the constraint had read where T : IComparable<T>, where both types, as well as other types, can meet that constraint.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
  • That error was not amongst those that appeared upon typing, but now I find yes it does appear when I build. "'double' is not a valid constraint." - thanks. I'd missed that. – ChrisJJ Aug 21 '11 at 00:03
  • Yes, this is an old answer, but `decimal` and `double` are struct's. And struct's are not a valid constraint. I just found that out. :( –  Aug 17 '16 at 23:39