Runtime: Add generic overloads to Math.Min and Math.Max

Created on 29 Apr 2015  路  9Comments  路  Source: dotnet/runtime

There are overloads of Math.Min and Math.Max for all the numeric types, but there aren't any for general comparable types (including framework types like DateTime and TimeSpan). It would be nice to have them.

Rationale

For integral types, Math.Min and Math.Max are not necessary, e.g. instead of Math.Min(a, b), you can write a <= b ? a : b. But they are nice, because they make the meaning clear and decrease the likelihood of error. The same argument applies to general comparable types, so they should work with Math.Min and Math.Max too.

Proposed API

``` c#
public static class Math
{
public static T Min(T val1, T val2) where T : IComparable;
public static T Min(T val1, T val2, IComparer comparer);

public static T Max<T>(T val1, T val2) where T : IComparable<T>;
public static T Max<T>(T val1, T val2, IComparer<T> comparer);

}
```

Open questions

  • Should the constraint where T : IComparable<T> be there? Other framework methods that require comparison, like Enumerable.OrderBy, don't have it and instead use Comparer<T>.Default.
  • Should nullable value types be supported? The proposed version doesn't, because T? does not implement IComparable<T?>, even when T does implement IComparable<T>.
api-needs-work area-System.Runtime up-for-grabs

Most helpful comment

I would ammend this API to move off of the Math class, for 3 reasons:

  • I think Math ceases to make as much sense with many IComparable<T> types. You could be comparing swimming pools or classical music to see which is greater, these things aren't bounded to mathematical concepts.
  • The overloads would be an overload change to existing code and we could have breaks there.
  • It avoids type forwarding concerns and allows the API to be added without a desktop CLR ship.

Instead, I'd suggest a new static class called Compare, where many such generic methods could live. Updated example:

C# public static class Compare { public static T Min<T>(T val1, T val2, IComparer<T> comparer = null); public static T Max<T>(T val1, T val2, IComparer<T> comparer = null); }

Should the constraint where T : IComparable<T> be there?

I would remove it and opt for a single overload that optionally accepts the IComparer<T>. If it's null, Comparer<T>.Default would be used for the same effect as the other overload.

Should nullable value types be supported?

I don't think they should be supported - because defining this behavior is...hard to define. You could argue that IComparable<T> forces such a comparison but structs remain fun and ambiguous.

All 9 comments

I would ammend this API to move off of the Math class, for 3 reasons:

  • I think Math ceases to make as much sense with many IComparable<T> types. You could be comparing swimming pools or classical music to see which is greater, these things aren't bounded to mathematical concepts.
  • The overloads would be an overload change to existing code and we could have breaks there.
  • It avoids type forwarding concerns and allows the API to be added without a desktop CLR ship.

Instead, I'd suggest a new static class called Compare, where many such generic methods could live. Updated example:

C# public static class Compare { public static T Min<T>(T val1, T val2, IComparer<T> comparer = null); public static T Max<T>(T val1, T val2, IComparer<T> comparer = null); }

Should the constraint where T : IComparable<T> be there?

I would remove it and opt for a single overload that optionally accepts the IComparer<T>. If it's null, Comparer<T>.Default would be used for the same effect as the other overload.

Should nullable value types be supported?

I don't think they should be supported - because defining this behavior is...hard to define. You could argue that IComparable<T> forces such a comparison but structs remain fun and ambiguous.

Should nullable value types be supported?

No because nullable value types can and often represent database nulls and you don't want to go there... As it means closer to unknown and the comparisons and behaviours are almost philosophical.

Is a null greater or less than a value? Or is it indeterminate depending on what the unknown value really is?

I would use params to calculate the max/min for any number of items. And in that vein, if you are already using a list of objects, then a LInQ extension kind of makes more sense. new[] { o1, o2, o3}.Max();

@Siderite that would require the an allocation of an array on each call. We definitely do not want that overhead. If such overhead is acceptable, then the current LINQ approach works.

Alternatively, we could provide such an overload, but alongside built-in non-array allocation overloads for (probably) 2, 3, and 4 parameters.

I always thought this was an antipattern resulting from the compiler laziness. Surely if there is a cost in allocating an array, then the compiler could create the underlying 1,2,3 parameter overloads for you. As such, with something like PostSharp, this could be implemented as a rule even by the developer, I think. Aren't you tired of writing Math.Max(Math.Max(a,b),c) when you want to compare three numbers? Of course, one can always create a Linq aggregate, but it feels like overkill.

How would the compiler generate the code here? That's different code for the multiple cases - you'd spend far more time writing the necessary (and even _if_ possible) compiler gymnastics here than the code for the few overloads :)

The whole point here (with any multi-overload method) is efficiency; optimizing for the 95-99% case with a few overloads and the rest can allocate more. If this wasn't a goal, we'd just all use the LINQ approach available right now.

@AlexGhiondea what is next here, are we asking @svick to update his proposal? or is this ready for review?

@danmosemsft I believe we were still settling on the design. I will take a look when I get back next year!

@svick @NickCraver I am closing this since we are going to review basically the same in dotnet/corefx#11555

Was this page helpful?
0 / 5 - 0 ratings