It would be logical if Math.Max and Math.Min had overloads for three, four and more parameters. I don't see why don't we have them already. They would be very easy to implement and backwards compatible.
When you need a method to take three non-negative ints you have to do one of the following:
Math.Min(Math.Min(a,b),c) < 0, which is really ugly and unreadableuints and cast them (which often results in double-casting because you have an int, you cast it to uint to pass it to the method and it casts it back to int again)Min method – this is the best solution but because you can't extend static classes, you'll end up with something awkward like MathExtensions.Min(a,b,c)Math.Min(a,b,c)I'll use a kind of pseudo-code. In actual implementation the number would have to be replaced by int, long, float, double, ...
```c#
public static class Math
{
public static number Max(number a, number b, number c);
public static number Max(number a, number b, number c, number d);
public static number Max(params number[] list);
public static number Min(number a, number b, number c);
public static number Min(number a, number b, number c, number d);
public static number Min(params number[] list);
}
```
Moved from dotnet/csharplang#1263.
Can you give some examples of when you need to do this over more than 2 values? I'm not suggesting there aren't scenarios.
The params case would allocate, I think. Would that be a perf issue in those scnearios?
As I said in the OP, the most frequent use case would probably be boundary checking for parameters.
When you have multiple parameters that have the same upper and/or lower boundary, you could just do if(Math.Max(a,b,c,d) >= 32) throw ArgumentOutOfRangeException("foo");. I don't think anyone would have more than four parameters with the same boundaries – I guess they'd use params instead (which means no perf issues here).
Also, you could do boundary checking on whole arrays. This would be probably done on places where performance isn't crucial and readability is more important. If you wanted to do poinwise operations on multiple numbers and performance was important, you would probably use System.Numerics.Vector<T> instead of an array, which could get its own SIMD-accelerated Max method in the future. (Or the developers could write one themselves.) But I guess Math.Max(params) wouldn't be the performance bottleneck here.
There are probably other uses but I can't think of any right now...
When you have multiple parameters that have the same upper and/or lower boundary, you could just do
if(Math.Max(a,b,c,d) >= 32) throw ArgumentOutOfRangeException("foo");
Personally, I'd probably be checking each parameter individually in that case, so I can list which one it was. There may be some non-parameter situations where it's useful; it ends up being somewhat common in aggregation scenarios where you're zipping multiple columns together into one result.
Also, you could do boundary checking on whole arrays. ....
We have this already: it's called LINQ.
if(Math.Max(a,b,c,d) >= 32) throw ArgumentOutOfRangeException("foo");
"foo" is wrong. Picking any other will be wrong 3 times out of 4, so you'd have to use the nullary constructor. This seems the worse of both worlds between the efficiency of if (a >= 32 | b >= 32 | c >= 32 | d >= 32) (or || rather than | if any calculation involved something that made short-circuiting better than avoiding branching, but either way it would likely beat calling even an inlined method more often not) and the precision of reporting the faulty argument.
@JonHanna
I would argue that nullary constructor for exceptions is a terrible idea. Especially when you can use descriptive messages like "All the dimensions/lengths/arguments/whatever have to be less than 32". Or if those aren't the only arguments and you don't know about any common name to call them, you can still throw "Arguments 2 through 5 have to be less than 32 each."
And yes, your version would be faster but let me paraphrase Pete Daukintis[1] here – often performance isn't such a big deal (often in UI applications that aren't computationally heavy) and even 60% overall slowdown is something the user wouldn't notice. In these cases, I think, we should give the developers freedom to write pretty code. And not repeating oneself is part of that.
Nullary call is better than a unary call with the wrong value.
I don't think I have needed 4 parameter ones so far, but several times I had to do 3 parameter ones e.g. finding the maximum resp. minimum of R, G, B.