Generics - where T is a number?

C#GenericsNumbers

C# Problem Overview


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.

C# Solutions


Solution 1 - C#

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
}

Solution 2 - C#

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.

Solution 3 - C#

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
(...)

Solution 4 - C#

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.

Solution 5 - C#

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);

Solution 6 - C#

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);
            }
           
        }
    }
}

Solution 7 - C#

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#

Solution 8 - C#

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCD..View Question on Stackoverflow
Solution 1 - C#Marc GravellView Answer on Stackoverflow
Solution 2 - C#GuffaView Answer on Stackoverflow
Solution 3 - C#EventHorizonView Answer on Stackoverflow
Solution 4 - C#Reed CopseyView Answer on Stackoverflow
Solution 5 - C#Robert HarveyView Answer on Stackoverflow
Solution 6 - C#Tanmay DesaiView Answer on Stackoverflow
Solution 7 - C#STWView Answer on Stackoverflow
Solution 8 - C#dmihailescuView Answer on Stackoverflow