10

Synopsis: I have a need to take two generic C# objects, and if they are numerical, compare them using either less than or greater than comparisons.

Problem: I can't figure out how to have my class implement IComparable as described in this post: Having to implement a generic less than and greater than operation. If that's not even the proper path to take, then I need to know that as well.

Background: I have implemented the RequiredIf ValidationAttribute found at A more complex custom validator but needed the > and < options in addition to an equals comparison.

Code (taken from A more complex custom validator, one-third down the page):

private bool IsRequired(object actualPropertyValue)
{
  switch (Comparison)
  {
    case Comparison.IsLessThan:
    case Comparison.IsLessThanOrEqualTo:
    case Comparison.IsGreaterThan:
    case Comparison.IsGreaterThanOrEqualTo:
      if (!Value.IsNumber())
      {
        throw new Exception("The selected comparison option is only applicable to numeric values");
      }
      break;
  }

  switch (Comparison)
  {
    case Comparison.IsNotEqualTo:
      return actualPropertyValue == null || !actualPropertyValue.Equals(Value);
    case Comparison.IsEqualTo:
      return actualPropertyValue != null && actualPropertyValue.Equals(Value);
    case Comparison.IsGreaterThan:
      // THIS LINE FAILS BECAUSE actualPropertyValue DOESN'T IMPLEMENT IComparable 
      return actualPropertyValue != null && (actualPropertyValue.CompareTo(Value) > 0); 
    // The rest of the comparison cases go here...
    default:
      throw new Exception("Comparison value is not defined");
  }
}

Static Helper Extensions:

public static bool IsNumber(this object value)
{
  if (value is sbyte) return true;
  if (value is byte) return true;
  if (value is short) return true;
  if (value is ushort) return true;
  if (value is int) return true;
  if (value is uint) return true;
  if (value is long) return true;
  if (value is ulong) return true;
  if (value is float) return true;
  if (value is double) return true;
  if (value is decimal) return true;
  return false;
}
Community
  • 1
  • 1
bigmac
  • 2,553
  • 6
  • 38
  • 61

2 Answers2

18

It sounds like you should just be able to cast actualPropertyValue to IComparable:

IComparable comparable = (IComparable) actualPropertyValue;
return comparable != null && comparable.CompareTo(Value) > 0;

Note that your use of the word "generic" is curious here. If you actually made it generic, you could write:

private bool IsRequired<T>(T actualPropertyValue) where T : IComparable

Then you wouldn't need the cast.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks Jon. I tried your second option and it seems to compile, so I'll continue to test that. However, when I implemented it, ReSharper warns me that the `actualPropertyValue == null` statements have a "possible compare of value type with null". Can that warning be disregarded? Secondly, based on the link that I am referencing, isn't it true that I am dealing with "generic" objects, or am I using the wrong terminology? – bigmac Jul 23 '12 at 16:05
4
int MyCompare (object a, object b)
{
   var ac = a as IComparable;
   var bc = b as IComparable;

    if (ac == null || bc == null)
       throw new NotSupportedException();

    return ac.CompareTo(bc);
 }
Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
  • Thanks Richard, but shouldn't the return line be `return ac.CompareTo(bc);`? – bigmac Jul 23 '12 at 17:21
  • Richard, I actually used Jon's method, but I'm keeping yours as a nice extension method for quick comparisons. Thanks for the help! – bigmac Jul 23 '12 at 18:50