C#: generic math functions (Min, Max etc.)
You probably want to constrain the generic types to implement IComparable
:
public T Max<T>(T v1, T v2) where T: struct, IComparable<T>
and then use the CompareTo
method:
{
return (v1.CompareTo(v2) > 0 ? v1 : v2);
}
If you only want to create comparison functions then you could use the default comparer for the type T
. For example:
public static T Max<T>(T x, T y)
{
return (Comparer<T>.Default.Compare(x, y) > 0) ? x : y;
}
If T
implements IComparable<T>
then that comparer will be used; if T
doesn't implement IComparable<T>
but does implement IComparable
then that comparer will be used; if T
doesn't implement either IComparable<T>
or IComparable
then a runtime exception will be thrown.
If you want/need to do more than just compare the items then you could have a look at the generic operators implementation in MiscUtil and the related article.
Let me disagree. The @LukeH's implementation is not Generic.
I will explain why it is not Generic:
Comparer<T>.Default
involves inspecting T at run-time to determine if it implements IComparable<T>
, IComparable
or neither.
Although this behavior is not well documented in http://msdn.microsoft.com/en-us/library/azhsac5f.aspx, we can deduct it because Comparer<T>.Default
throws an exception when T does not implement neither. If the inspection was done at compilation-time there would be no need for an exception (runtime), with an compile-time error would suffice.
Then, as Comparer<T>.Default
uses Reflection, this means a high cost on Run-Time, then...., It is NOT Generic... Why?
Because Generic Programming means: A single algorithm (Generic) can cover many implementations (for many types) maintaining efficiency of hand-written versions.
Take an example. The handwritten version for integers would be:
public static int Max( int x, int y)
{
return (x.CompareTo(y) > 0) ? x : y;
}
It is very simple, involving only a comparison (or maybe more, depending on how Int32.CompareTo() is implemented). If we use the @LukeH's implementation, we are adding Reflection to something that is very simple.
In short:
- Always prefer Compilation-time errors to Run-Time Exceptions ( this is not Javascript, Ruby,... :-) )
- This implementation is less efficient compared to the handwritten version (for any type)
On the other hand. What do you think Max should return when x and y are equivalents?
I'm starting to analyze Real-Generic implementations....
The ideal implementation would be something like...
public static T Max<T>(T x, T y, Func<T, T, int> cmp)
{
return (cmp(x, y) > 0) ? x : y;
}
//Pseudo-code ( note the 'or' next to 'where' )
public static T Max<T>(T x, T y) where T: IComparable<T> or IComparable
{
return Max(x, y, (a, b) => { return a.CompareTo(b); });
}
This is not possible in C#, the next try is could be...
//pseudo-code
public static T Max<T>(T x, T y, Func<T, T, int> cmp)
{
return (cmp(x, y) > 0) ? x : y;
}
public static T Max<T>(T x, T y) where T: IComparable<T>
{
return Max(x, y, (a, b) => { return a.CompareTo(b); });
}
public static T Max<T>(T x, T y) where T: IComparable
{
return Max(x, y, (a, b) => { return a.CompareTo(b); });
}
But, this is neither possible, because overload resolution doesn't takes into account Generics Constraints....
Then, I'll leave out IComparable
consciously. I'm just going to worry about IComparable<T>
public static T Max<T>(T x, T y, Func<T, T, int> cmp)
{
return (cmp(x, y) > 0) ? x : y;
}
public static T Max<T>(T x, T y) where T: IComparable<T>
{
return Max(x, y, (a, b) => { return a.CompareTo(b); });
}