Home > Software design >  Min of two numeric types (including nullable)
Min of two numeric types (including nullable)

Time:01-28

I want a method that returns the smallest of two values, when one value is null it should return the non-null value, and when both are null it should return null.

I created a helper class with the following method public static T? Min<T>(T? a, T? b)

I tried to make this very flexible (also support strings etc) by adding the constraints where T : IEquatable<T>, IComparable<T>, but these won't allow me to pass int arguments.

I then tried where T : INumber<T>. This worked with int arguments, but not with Nullable<int> because that type doesn't satisfy the constraint INumber<Nullable<int>>.

Is there a way to achieve this?

CodePudding user response:

Since you want T? to mean Nullable<T>, you need to add the struct constraint:

public static T? Min<T>(T? a, T? b) where T : struct, IEquatable<T>, IComparable<T> 
{ 
    // do your magic here
}

Calling it both with int and int? parameters compiles successfully (fiddle):

    int a = 2;
    int? b = 2;
    
    int? c = Min<int>(a, a);
    int? d = Min(a, b);
    int? e = Min(b, b);

Apparently, type inference does not work well if both parameters are int (maybe because there are other suitable implicit conversions available than those to int??). If that bothers you, you can add a non-nullable T Min<T>(T a, T b) overload that handles this special case.

CodePudding user response:

In my experience, generics does not work very well with Nullable. I have not found any way to treat a reference type and a Nullable struct the same way, but I'm not up to date with the latest .Net version.

Your code should work for most numeric types if you specify a struct-constraint

public static T? Min<T>(T? a, T? b) where T : struct, IComparable<T>

You could then declare a separate method restricted to classes

public static T Min<T>(T a, T b) where T : class, IComparable<T>

But there might be situations where the overload resolution has issue picking the correct overload. An alternative would be to declare your own Maybe<T>/Option<T>-type, possibly with an implicit conversion from T. That should allow you to use the same generic method for value and reference types.

CodePudding user response:

I suggest implementing two methods: first is for Nullable<T> and second for all the rest. Assuming that

  • null is ever less than any not null in case of Nullable<T>
  • if left equals to right we return left

we can put it like this:

// Nullable<T> is a special case, we should take
// HasValue and Value into account
public static T? Min<T>(T? left, T? right) where T : struct, IComparable<T> {
  if (left.HasValue && right.HasValue)
    return Comparer<T>.Default.Compare(left.Value, right.Value) > 0
      ? left.Value
      : right.Value;

  return right.HasValue 
    ? right 
    : left;
}

// All the rest. Business as usual
// Note Comparer to be able to compare nulls
public static T Min<T>(T left, T right) where T : IComparable<T> =>
  Comparer<T>.Default.Compare(left, right) > 0 ? right : left;

Demo:

int? a = null;
int b = 123;

// 123
Console.WriteLine(Min(a, b));
  • Related