سؤال

I am sure that there is a simple explanation but cannot work out the following:

     const short amount = 30000;
     bool isGreater =
             ComparableExtensions.IsGreaterThan(amount, 29000); //returns true

     bool isGreaterThan2 = 
     amount.IsGreaterThan<short>(29000);//returns false

      public static class ComparableExtensions
      {
            public static bool IsGreaterThan<T>(this T leftValue, T rightValue) 
            where T : struct, IComparable<T>
            {
                var result = leftValue.CompareTo(rightValue) == 1;
                return result;
            }

      }

Is it because i put a "Struct" contraint?

any explanation or suggestions?

thanks

هل كانت مفيدة؟

المحلول

No, your mistake using leftValue.CompareTo(rightValue) == 1.

Instead, say leftValue.CompareTo(rightValue) > 0.

All you know is that CompareTo returns < 0 if leftValue is less than rightValue, 0 if leftValue equals rightValue and > 0 if leftValue is greater than rightValue. You can not only check for equality with 1.

Additionally, the reason that you see different behavior between the two calls is because in the first case you are calling IsGreaterThan<int> because the literal constant 29000 will be interpreted as Int32, but in the second case you explicitly say short so it will be interpreted as Int16.

نصائح أخرى

Look at the documentation for IComparable, and the return value of the CompareTo method. It states that it will return a value less than zero if a < b, exactly zero if a == b, and a value greater than zero if a > b. Yet you are comparing the return value to 1. There is no guarantee this method will ever return 1. You should instead do:

var result = leftValue.CompareTo(rightValue) > 0;

The current short.CompareTo() implementation is something like this:

public int CompareTo(short value)
{
    return (int)(this - value);
}

while the int.CompareTo() implementation is something like this:

public int CompareTo(int value)
{
    if (this < value)
    {
       return -1;
    }
    if (this > value)
    {
       return 1;
    }
    return 0;
}

In the first case the generic T type is inferred as Int32 because 29000 is an Int32 string literal and so the int.CompareTo() method is called.

In the second case you're specifically saying that T is short and then the short.CompareTo() is called.

As other answers already pointed out, IComparable<T> specification doesn't assure to return always:

 0 : if the numbers are equal  
-1 : if the number is lower than the other
 1 : if the number is greater than the other

but just says that it must return:

0            : if the numbers are equal  
a number < 0 : if the number is lower than the other
a number > 0 : if the number is greater than the other

The compiler infers the generic type in the first (static) call. It uses Int32. The Int32.CompareTo method returns 1 for greater than.

In the second, your call from an Int16 forces T to be Int16. Int16.CompareTo returns the actual difference between the two values*. Legal under IComparable (which only requires a value > 0) - but different enough for your call to fail.

Explicitly casting the first call to short will cause it to fail as well.

* No, I have no idea why Int16 is implemented that way. Seems to be "because it can be"(Byte behaves the same) - since you can subtract without fear of overflow (since you're casting to a wider type for the result). It'd require more work to do something similar for Int32 which I suppose wasn't worth the effort.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top