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:

  1. Always prefer Compilation-time errors to Run-Time Exceptions ( this is not Javascript, Ruby,... :-) )
  2. 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); });
    }

Tags:

C#

Math