Reputation: 16040
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
Upvotes: 1
Views: 100
Reputation: 85665
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.
Upvotes: 0
Reputation: 57210
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
Upvotes: 0
Reputation: 241641
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
.
Upvotes: 4
Reputation: 6096
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;
Upvotes: 0