-1

I've got a method like this:

public static bool IsPercentage<T>(T value) where T : IComparable
{
    return value.CompareTo(0) >= 0 && value.CompareTo(1) <= 0;
}

I would like to use this to validate if any number falls in the range 0 <= N <= 1. However this only works with integers since CompareTo only operates on equal types. Is there a different way to do this?

ConditionRacer
  • 4,418
  • 6
  • 45
  • 67
  • What types of objects do you expect this to work? Float, Double, Decimal and ...? – Thomas Weller Feb 05 '14 at 20:23
  • @ThomasW. Preferably any numeric type – ConditionRacer Feb 05 '14 at 20:24
  • @Servy I think it has nothing to do with the question you've set as *duplicate of*. OP doesn't need the method to only accept `T` as `int` (what's the point of having a generic method for only one type?!). He needs the `CompareTo` to get properly typed `0` and `1` value: `0`/`1` for `T==int`, `0d`/`1d` for `T==double`, etc. – MarcinJuraszek Feb 05 '14 at 20:41
  • @MarcinJuraszek Sounds like you only read the title, and didn't look at the actual question. – Servy Feb 05 '14 at 20:43
  • I read this from question: *I would like to use this to validate if any number falls in the range 0 <= N <= 1.* and from comments: *What types of objects do you expect this to work? Float, Double, Decimal and ...?* with *Preferably any numeric type* as a response. – MarcinJuraszek Feb 05 '14 at 20:45
  • @MarcinJuraszek I mean you didn't read the question that I proposed as a duplicate. You just looked at the title. If you clicked the link to see what it's really asking, you'd see it clearly answers this question. – Servy Feb 05 '14 at 20:46
  • It says: you can't set generic constraint to do this. But it doesn't actually mean you can't make the method work. – MarcinJuraszek Feb 05 '14 at 20:48
  • @MarcinJuraszek There is no way to ensure that the method is only called with a type for which this will work. Any solutions would, by necessity, have to do the checks at runtime, not compile time, and thus not be able to statically validate the input. – Servy Feb 05 '14 at 20:50

2 Answers2

1

You can use Expression Tree to do this. Consider helper, static class

static class NumericHelper<T>
{
    public static T Zero { get; private set; }
    public static T One { get; private set; }

    static NumericHelper()
    {
        Zero = default(T);
        One = Expression.Lambda<Func<T>>(
                Expression.Convert(
                    Expression.Constant(1),
                    typeof(T)
                )
              ).Compile()();
    }
}

It generates (T)1 cast at runtime and assign result to One property. Because static constructor is fired only once code necessary to generate properly typed 1 value will be executed only once for every T.

public static bool IsPercentage<T>(T value) where T : IComparable
{
    return value.CompareTo(NumericHelper<T>.Zero) >= 0 && value.CompareTo(NumericHelper<T>.One) <= 0;
}

Ofc, it will fail if you try to call it with type T which don't support (T)1 conversion.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
1

well you could use Convert.ToDecimal, then you don't need to be generic:

public static bool IsPercentage(Object value)
{
    decimal val = 0;
    try
    {
        val = Convert.ToDecimal(value);
    }
    catch
    {
        return false;
    }
    return val >= 0m && val <= 1m;
}
D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • This returns `true` for `1.1m` – Servy Feb 05 '14 at 20:45
  • @Servy {facepalm} thanks - changed to Decimal – D Stanley Feb 05 '14 at 20:53
  • 1
    Note that with this code there are certain float/double values that are just barely larger than one that, when converted to a decimal, are considered equal to one. For example, call this method with `1f + float.Epsilon`. It will return true, despite being greater than one. The same applies to `1d + double.Epsilon`. – Servy Feb 05 '14 at 20:56
  • That's quite an inefficient function. You've got a boxing/unboxing action and a conversion for any non decimal type within an exception block. Given that there's only 3 types this method will be called with (float/double/decimal) this is overkill. – Sean Feb 06 '14 at 09:05