97

I'm trying to write an extension method on numeric types to be used in a fluent testing framework I'm building. Basically, I want to do this:

public static ShouldBeGreaterThan<T>(this T actual, T expected, string message)
    where T : int || T: double || etc...

Just where T : struct doesn't do, since that will also match string and bool, and possibly something else I'm forgetting. is there something I can do to match only numeric types? (Specifically types that implement the > and < operators, so I can compare them... If this means I'm matching dates as well, it doesn't really matter - the extension will still do what I expect.)

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • 6
    Jon Skeet and Mark Gravell put together some interesting classes for this: http://www.yoda.arachsys.com/csharp/genericoperators.html – Dan Bryant Jul 25 '10 at 14:52
  • 13
    @Dan actually it is "Marc", but I'll let you off - very few people get it right ;p – Marc Gravell Jul 25 '10 at 15:13
  • 4
    that link is dead. use this instead https://jonskeet.uk/csharp/miscutil/ https://jonskeet.uk/csharp/genericoperators.html – Zar Shardan Jul 23 '19 at 12:25

6 Answers6

65

In this case you want to constrain your generic to the IComparable interface, which gives you access to the CompareTo method, since this interface allows you to answer the question ShouldBeGreaterThan.

Numeric types will implement that interface and the fact that it also works on strings shouldn't bother you that much.

Per Lundberg
  • 3,837
  • 1
  • 36
  • 46
flq
  • 22,247
  • 8
  • 55
  • 77
  • 1
    This is a better solution than a more general operator interface for numeric types, at least for this problem. – Dan Bryant Jul 25 '10 at 14:55
  • 1
    Is there a way I can allow the usage of `==` and `>` etc rather than `.Equals()` and `.CompareTo`? – Aaron Franke Feb 27 '18 at 22:16
  • 1
    Those operators are implemented as static methods - that is, currently C# has no way to constrain on static features of a type, – flq Jul 28 '20 at 07:43
  • Note that `IComparable` only seems to work when comparing objects of the same type. It does not seem to work to call `123.CompareTo(123L)`, for example, which can be a problem depending on what you are trying to achieve. – Per Lundberg Nov 19 '21 at 21:33
58
where T : struct, 
          IComparable, 
          IComparable<T>, 
          IConvertible, 
          IEquatable<T>, 
          IFormattable

That's the closest I can get to a numeric constraint. All the numeric types implement these 5 interfaces, but IFormattable is not implemented by bool, and strings are a reference type, so they're not applicable.

There's some other things that implement these - DateTime for example, so it's not really as required, but prevents a lot of instantiations you don't want.

Mark H
  • 13,797
  • 4
  • 31
  • 45
  • 4
    Even with this, I cannot seem to use common math operations. `Operator '==' cannot be applied to operands of type 'T' and 'float'` – Aaron Franke Feb 27 '18 at 22:13
21
public static bool IsGreaterThan<T>(this T actual, T comp) where T : IComparable<T>
{
    return actual.CompareTo(comp) > 0;
}

You can add the struct constraint if you want as well.

Lee
  • 142,018
  • 20
  • 234
  • 287
9

It is hard to limit to just numerics, since there is nothing common like INumeric to use as the filter. Actually, I suspect the easiest approach here is to not insist on the constraint, and use Comparer<T>.Default.Compare inside the method.

This inbuilt type supports both the generic IComparable<T> and the non-generic IComparable, and supports ref-types, value-types and lifted usage via Nullable<T>.

For full operator usage, look at MiscUtil's Operator class and GreaterThan etc, which may be useful if you really want to use the operator (rather than the interface). It also provides access to the other operators like Add etc.

Kit
  • 20,354
  • 4
  • 60
  • 103
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Marc Gravell said "It is **hard** to limit to just numerics" but it is **possible** both in run time and compile time. Please see my answer to "Is there a constraint that restricts my generic method to numeric types?" question.https://stackoverflow.com/questions/32664/is-there-a-constraint-that-restricts-my-generic-method-to-numeric-types –  Aug 19 '17 at 20:02
  • 1
    @Erez that'll work, but is death to performance critical code - lots of boxing and type checking. Somewhere in the C#7.2-C#8.0 period we are likely to see additions to generics to do this much more efficiently. – Marc Gravell Aug 20 '17 at 10:19
  • I can't wait until this will be supported by Microsoft. –  Aug 20 '17 at 12:15
5

Stackoverflow is littered with this kind of question. Take a look at this search. C# doesn't support a way to define a generic type constrained by numbers. Sadly, your best bet is to implement the extension method on all objects and do a switch based on type or to create a set of methods for ints, doubles, floats, etc.

Community
  • 1
  • 1
Jake Pearson
  • 27,069
  • 12
  • 75
  • 95
0

This workaround may help: Workaround using policies. It provides compile time safety.

Community
  • 1
  • 1
Sergey Shandar
  • 2,357
  • 18
  • 25