Search code examples
c#.netgenericscomparison-operators

How can I make this simple method into a generic method?


I have a method as follows:

int coerce(int val, int min=0, int max = 10)
{
    if (val < min)
        return min;
    if (val > max)
        return max;
    return val;
}

Now, I have to make it for byte, float, double, and other numeric types.

As all we know, making numerous similar methods for those types is very ineffective, so I want to make it into a generic method. The following is the what I tried to do:

T coerce<T>(T val, T min=(T)0, T max=(T)10) where T:IComparable
{
    // ... same as the above ...
}

I know that the code does not run, and that's why I'm asking for this. I'm currently confused by two questions:

  1. How can I compare T types?

    Visual Studio warns about the operator < and >. I tried to use where T:IComparable but it did not solve my problem.

  2. How can I set default values for a generic argument?

    I tried to use (T)0 and (T)10 for it. But it was not a good choice, anyway.

It may be a simple question, but I couldn't find answer from Google.


Solution

  • You should use Compare method instead of < or > operators. And apply the correct generic constraints, for numeric types it should be IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable. However, you can leave only where T : struct, IComparable<T>, it should be enough for your purposes (but struct is important here, since you are comparing value types)

    T coerce<T>(T val, T min, T max) where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
    {
        if (val.CompareTo(min) < 0)
            return min;
        if (val.CompareTo(max) > 0)
            return max;
    
        return val;
    }
    

    You can also specify the default min value like T min = default(T), but you can't do that for max value.

    Following the comments, in case of using Nullable<T> for min and max values the code can be written like

    T coerce<T>(T val, T? min = default, T? max = default) where T : struct, IComparable<T>
    {
        var minValue = min.HasValue ? min.Value : default(T);
        var maxValue = max.HasValue ? max.Value : (T)Convert.ChangeType(10, typeof(T));
    
        if (val.CompareTo(minValue) < 0)
            return minValue;
        if (val.CompareTo(minValue) > 0)
            return maxValue;
    
        return val;
    }