74

I'm trying to figure a way to create a generic class for number types only, for doing some calculations.

Is there a common interface for all number types (int, double, float...) that I'm missing???

If not, what will be the best way to create such a class?

UPDATE:

The main thing I'm trying to achieve is checking who is the bigger between two variables of type T.

CD..
  • 72,281
  • 25
  • 154
  • 163
  • 1
    They don't work in this scenario, but when you're working with Generics it's good to be aware of "generic type constraints". http://msdn.microsoft.com/en-us/library/d5x73970%28VS.80%29.aspx – STW Aug 12 '09 at 18:56
  • 1
    Check this post here http://stackoverflow.com/questions/32664/c-generic-constraint-for-only-integers – David Aug 12 '09 at 18:33
  • 1
    As far as I know this is not possible. [You may find some reasons why (and alternative solutions) here](http://stackoverflow.com/questions/32664/c-generic-constraint-for-only-integers). – Fredrik Mörk Aug 12 '09 at 18:33
  • 2
    Have you tried IConvertible / IComparible? -- All the number classes support it. – BrainSlugs83 Aug 29 '14 at 06:57

8 Answers8

36

What version of .NET are you using? If you are using .NET 3.5, then I have a generic operators implementation in MiscUtil (free etc).

This has methods like T Add<T>(T x, T y), and other variants for arithmetic on different types (like DateTime + TimeSpan).

Additionally, this works for all the inbuilt, lifted and bespoke operators, and caches the delegate for performance.

Some additional background on why this is tricky is here.

You may also want to know that dynamic (4.0) sort-of solves this issue indirectly too - i.e.

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect

Re the comment about < / > - you don't actually need operators for this; you just need:

T x = ..., T y = ...
int c = Comparer<T>.Default.Compare(x,y);
if(c < 0) {
    // x < y
} else if (c > 0) { 
    // x > y
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
18

There are interfaces for some of the operations on the number types, like the IComparable<T>, IConvertible and IEquatable<T> interfaces. You can specify that to get a specific functionality:

public class MaxFinder<T> where T : IComparable<T> {

   public T FindMax(IEnumerable<T> items) {
      T result = default(T);
      bool first = true;
      foreach (T item in items) {
         if (first) {
            result = item;
            first = false;
         } else {
            if (item.CompareTo(result) > 0) {
               result = item;
            }
         }
      }
      return result;
   }

}

You can use delegates to expand a class with type specific operations:

public class Adder<T> {

   public delegate T AddDelegate(T item1, T item2);

   public T AddAll(IEnumerable<T> items, AddDelegate add) {
      T result = default(T);
      foreach (T item in items) {
         result = add(result, item);
      }
      return result;
   }

}

Usage:

Adder<int> adder = new Adder<int>();
int[] list = { 1, 2, 3 };
int sum = adder.AddAll(list, delegate(int x, int y) { return x + y; });

You can also store delegates in the class, and have different factory methods that sets up delegates for a specific data type. That way the type specific code is only in the factory methods.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
12

Closest you get is struct I'm afraid. You'll have to do more extensive checks for number types in code.

public class MyClass<T> where T : struct
(...)
EventHorizon
  • 2,916
  • 22
  • 30
10

You cannot do this, since you'd have to use a single interface for arithmetic operations. There have been many requests on Connect to add an IArithmetic interface for this specific purpose, but so far they've all been rejected.

You can sort of work around this by defining a struct with no members, which implements a "Calculator" interface. We took this approach in an interpolation generic class in the Pluto Toolkit. For a detailed example, we have a "vector" calculator implementation here, which lets our generic interpolator work with vectors. There are similar ones for floats, doubles, quaternions, etc.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
7
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericPratice1
{
    public delegate T Del<T>(T numone, T numtwo)where T:struct;
    class Class1
    {
        public T Addition<T>(T numone, T numtwo) where T:struct
        {
            return ((dynamic)numone + (dynamic)numtwo);
        }
        public T Substraction<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone - (dynamic)numtwo);
        }
        public T Division<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone / (dynamic)numtwo);
        }
        public T Multiplication<T>(T numone, T numtwo) where T : struct
        {
            return ((dynamic)numone * (dynamic)numtwo);
        }

        public Del<T> GetMethodInt<T>(int ch)  where T:struct
        {
            Console.WriteLine("Enter the NumberOne::");
            T numone =(T) Convert.ChangeType((object)(Console.ReadLine()), typeof(T));
            Console.WriteLine("Enter the NumberTwo::");
            T numtwo = (T)Convert.ChangeType((object)(Console.ReadLine()), typeof(T));
            T result = default(T);
            Class1 c = this;
            Del<T> deleg = null;
            switch (ch)
            {
                case 1:
                    deleg = c.Addition<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 2: deleg = c.Substraction<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 3: deleg = c.Division<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                case 4: deleg = c.Multiplication<T>;
                    result = deleg.Invoke(numone, numtwo);
                    break;
                default:
                    Console.WriteLine("Invalid entry");
                    break;
            }
            Console.WriteLine("Result is:: " + result);
            return deleg;
        }

    }
    class Calculator
    {
        public static void Main(string[] args)
        {
            Class1 cs = new Class1();
            Console.WriteLine("Enter the DataType choice:");
            Console.WriteLine("1 : Int\n2 : Float");
            int sel = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine("Enter the choice::");
            Console.WriteLine("1 : Addition\n2 : Substraction\3 : Division\4 : Multiplication");
            int ch = Convert.ToInt32(Console.ReadLine());
            if (sel == 1)
            {
                cs.GetMethodInt<int>(ch);
            }
            else
            {
                cs.GetMethodInt<float>(ch);
            }

        }
    }
}
Tanmay Desai
  • 93
  • 1
  • 5
6

In the Framework BCL (base class library), many numeric functions (such as the functions in System.Math) deal with this by having overloads for each numeric type.

The static Math class in the BCL contains static methods, which you can call without having to create an instance of the class. You could do the same in your class. For example, Math.Max has 11 overloads:

public static byte Max(byte val1, byte val2);
public static decimal Max(decimal val1, decimal val2);
public static double Max(double val1, double val2);
public static short Max(short val1, short val2);
public static int Max(int val1, int val2);
public static long Max(long val1, long val2);
public static sbyte Max(sbyte val1, sbyte val2);
public static float Max(float val1, float val2);
public static ushort Max(ushort val1, ushort val2);
public static uint Max(uint val1, uint val2);
public static ulong Max(ulong val1, ulong val2);
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
3

I don't believe you can define that using a generic type constraint. Your code could internally check your requirements, possibly using Double.Parse or Double.TryParse to determine if it is a number--or if VB.NET isn't out of the question then you could use the IsNumeric() function.

Edit: You can add a reference to Microsoft.VisualBasic.dll and call the IsNumeric() function from c#

STW
  • 44,917
  • 17
  • 105
  • 161
3

You can not do it at compile time only. But you could put more constraints to weed out most of 'bad types' on your numeric type like below

class yourclass <T>where T: IComparable, IFormattable, IConvertible, IComparabe<T>, IEquatable<T>, struct {... 

In the end you would still have to check at runtime if your type is acceptable using object.GetType() method.

If only comparing, then IComparable<T> alone does the trick.

davidsbro
  • 2,761
  • 4
  • 23
  • 33
dmihailescu
  • 1,625
  • 17
  • 15