Genéricos: ¿dónde T es un número?

Estoy tratando de encontrar la manera de crear una clase genérica solo para tipos de números, para hacer algunos cálculos.

¿Hay una interfaz común para todos los tipos de número (int, double, float …) que me falta?

Si no, ¿cuál será la mejor manera de crear una clase así?

ACTUALIZAR:

Lo principal que trato de lograr es verificar quién es el más grande entre dos variables de tipo T.

¿Qué versión de .NET estás usando? Si está usando .NET 3.5, entonces tengo una implementación de operadores generics en MiscUtil (gratis, etc.).

Esto tiene métodos como T Add(T x, T y) y otras variantes de aritmética en diferentes tipos (como DateTime + TimeSpan ).

Además, esto funciona para todos los operadores incorporados, levantados y personalizados, y almacena en caché al delegado para el rendimiento.

Algunos antecedentes sobre por qué esto es complicado está aquí .

También querrá saber que el tipo dynamic (4.0) también resuelve este problema indirectamente, es decir,

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

Re el comentario sobre < / > : en realidad no necesitas operadores para esto; Solo necesitas:

 T x = ..., T y = ... int c = Comparer.Default.Compare(x,y); if(c < 0) { // x < y } else if (c > 0) { // x > y } 

Existen interfaces para algunas de las operaciones en los tipos de números, como las interfaces IComparable , IConvertible e IEquatable . Puede especificar eso para obtener una funcionalidad específica:

 public class MaxFinder where T : IComparable { public T FindMax(IEnumerable 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; } } 

Puede usar delegates para expandir una clase con operaciones específicas de tipo:

 public class Adder { public delegate T AddDelegate(T item1, T item2); public T AddAll(IEnumerable items, AddDelegate add) { T result = default(T); foreach (T item in items) { result = add(result, item); } return result; } } 

Uso:

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

También puede almacenar delegates en la clase y tener diferentes métodos de fábrica que configuren delegates para un tipo de datos específico. De esta forma, el código específico del tipo solo está en los métodos de fábrica.

No puede hacer esto, ya que tendría que usar una única interfaz para operaciones aritméticas. Ha habido muchas solicitudes en Connect para agregar una interfaz IArithmetic para este propósito específico, pero hasta ahora todas han sido rechazadas.

Puede solucionar este problema definiendo una estructura sin miembros, que implementa una interfaz de “Calculadora”. Tomamos este enfoque en una clase genérica de interpolación en el Pluto Toolkit . Para un ejemplo detallado, tenemos aquí una implementación de calculadora “vectorial”, que permite que nuestro interpolador genérico funcione con vectores. Hay similares para carrozas, dobles, cuaterniones, etc.

Lo más cerca que tienes es struct, me temo. Tendrá que hacer comprobaciones más exhaustivas para los tipos de números en el código.

 public class MyClass where T : struct (...) 

En Framework BCL (biblioteca de clases base), muchas funciones numéricas (como las funciones en System.Math) tratan esto teniendo sobrecargas para cada tipo numérico.

La clase matemática estática en el BCL contiene métodos estáticos, a los que puede llamar sin tener que crear una instancia de la clase. Podrías hacer lo mismo en tu clase. Por ejemplo, Math.Max ​​tiene 11 sobrecargas:

 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); 
 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace GenericPratice1 { public delegate T Del(T numone, T numtwo)where T:struct; class Class1 { public T Addition(T numone, T numtwo) where T:struct { return ((dynamic)numone + (dynamic)numtwo); } public T Substraction(T numone, T numtwo) where T : struct { return ((dynamic)numone - (dynamic)numtwo); } public T Division(T numone, T numtwo) where T : struct { return ((dynamic)numone / (dynamic)numtwo); } public T Multiplication(T numone, T numtwo) where T : struct { return ((dynamic)numone * (dynamic)numtwo); } public Del GetMethodInt(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 deleg = null; switch (ch) { case 1: deleg = c.Addition; result = deleg.Invoke(numone, numtwo); break; case 2: deleg = c.Substraction; result = deleg.Invoke(numone, numtwo); break; case 3: deleg = c.Division; result = deleg.Invoke(numone, numtwo); break; case 4: deleg = c.Multiplication; 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(ch); } else { cs.GetMethodInt(ch); } } } } 

No creo que pueda definir eso usando una restricción de tipo genérico. Su código podría verificar internamente sus requisitos, posiblemente utilizando Double.Parse o Double.TryParse para determinar si se trata de un número– o si VB.NET no está descartado, entonces podría usar la función IsNumeric ().

Editar: puede agregar una referencia a Microsoft.VisualBasic.dll y llamar a la función IsNumeric () desde c #

No puedes hacerlo solo en tiempo de comstackción. Pero podría poner más restricciones para descartar la mayoría de los “tipos malos” en su tipo numérico, como a continuación

class yourclass donde T: IComparable, IFormattable, IConvertible, IComparabe , IEtabletable , struct {… Al final, igual deberías verificar en tiempo de ejecución si tu tipo es aceptable usando object.GetType ( ) método.

Si solo comparas, entonces IComparable solo hace el truco.