2009-05-06 17 views
39

Digamos que quiero tener un método que tenga cualquier tipo de número, ¿hay alguna clase base (o algún otro concepto) que pueda usar?¿Cuál es la "clase base" para los tipos de valores numéricos C#?

Hasta donde yo sé, tengo que hacer sobrecargas para todos los diferentes tipos numéricos (Int32, Int16, Byte, UInt32, Doble, Flotante, Decimal, etc.). Esto parece terriblemente tedioso. O eso, o usa el tipo "objeto" y lanza excepciones si no son convertibles o asignables a un doble, lo cual es bastante malo, ya que significa que no hay tiempo de compilación.

ACTUALIZACIÓN: OK gracias por los comentarios, que están justo espantapájaros y Marc, de hecho declarar como Doble realmente funciona para todos, excepto decimal.

Así que la respuesta que estaba buscando es Doble: aquí actúa como una clase base, ya que la mayoría de los tipos numéricos se pueden asignar a ella. (Supongo decimal no es asignable al doble, ya que podría ser demasiado grande.)

public void TestFormatDollars() { 
    int i = 5; 
    string str = FormatDollars(i); // this is OK 
    byte b = 5; 
    str = FormatDollars(b);  // this is OK 
    decimal d = 5; 
    str = FormatDollars(d);  // this does not compile - decimal is not assignable to double 
} 

public static string FormatDollars(double num) { 
    return "$" + num; 
} 
+1

¿Qué tipos numéricos no son asignables a doblar? (Excepto por las grandes clases de números que admiten números arbitrariamente grandes) –

+1

@scarecrow - decimal es de 96 bits ... muchos otros simplemente no son buenos para el rango/escala. –

+0

¿Quizás podría aclarar qué quiere hacer con los números dentro del método? –

Respuesta

19

La respuesta es: no es necesario para proporcionar sobrecargas para todos los tipos numéricos, sólo para doble y decimal. Todos los demás (excepto quizás algunos muy inusualmente grandes) se convertirán automáticamente a estos.

No es clase base pero de hecho esa era la pista falsa. La clase base System.ValueType no ayuda mucho ya que incluye tipos que no son numéricos.La referencia de idioma que estaba leyendo fue lo que me confundió en primer lugar :)

(Estaba buscando a quién atribuir la respuesta y era una combinación de Scarecrow y Marc Gravell, pero ya que eran comentarios i han puesto la respuesta aquí)

+3

En realidad, esto proporciona firmas de método ambiguas. Sobrecargar uno de ellos hace el truco. –

13

no hay uno (o al menos, no uno que simplemente significa "números"). Se podría utilizar:

void Foo<T>(T value) where T : struct {...} 

Pero eso permite cualquier estructura - no sólo los números. Si desea hacer aritmética, puede ser útil generic operators. Aparte de eso; lo sobrecarga la opción más viable.

5

La clase base de los tipos numéricos es ValueType.

desgracia que aún no le ayudará: DateTime, bool, Enum y cientos de otros tipos también se derivan de ValueType. No hay clase base NumericType en .NET.

+0

Los tipos de valor no son necesariamente números, ¿o sí? –

+0

ValueType también incluye cadenas y otros tipos de valores no numéricos, sin embargo. – Noldorin

+0

@Noldorin, tiene razón en que ValueType incluye cientos de tipos no numéricos, sin embargo, string no es uno de ellos. String es un tipo de referencia (simplemente se comporta como un tipo de valor). – LukeH

10

La respuesta corta es: Los tipos numéricos son tipos de valores, por lo tanto, se derivan de System.ValueType. La respuesta completa es: debe leer esto article from MSDN. Además, creo que deberías leer la referencia del lenguaje C# :). El tipo de valor no es igual al tipo numérico, porque los tipos de valores también incluyen estructuras y enumeraciones.

+0

El artículo de MSDN realmente no considera cuál es el mejor tipo para usar cuando se quiere que un parámetro capture todos los tipos de números y nada más, que es lo que estaba preguntando. –

+0

Sí, pero el artículo muestra que el tipo decimal y otros tipos de coma flotante son tipos diferentes de tipos. –

0

¿Hay sobreinformación sobre métodos sobrecargados aquí? Si desea que un grupo restringido de métodos realice la misma tarea, puede descargar el método público y llamar a un método privado que toma cualquier número al convertir la entrada en un doble.

+1

Formalmente, eso sería una conversión, no un reparto. –

+0

Bueno, no es imposible, solo molesto! Dado que hay muchos tipos numéricos. Esta parece ser la mejor solución, pero me preguntaba si había una mejor manera. –

1

lo que hago:

public interface INumeric<T> 
{ 
    T Zero { get; } 
    T One { get; } 
    T MaxValue { get; } 
    T MinValue { get; } 
    T Add(T a, T b); 
    // T Substract(.... 
    // T Mult... 
} 

public struct Numeric: 
    INumeric<int>, 
    INumeric<float>, 
    INumeric<byte>, 
    INumeric<decimal>, 
    // INumeric<other types> 
{ 
    int INumeric<int>.Zero => 0; 
    int INumeric<int>.One => 1; 
    int INumeric<int>.MinValue => int.MinValue; 
    int INumeric<int>.MaxValue => int.MaxValue; 
    int INumeric<int>.Add(int x, int y) => x + y; 

    // other implementations... 
} 

Ahora, usted puede utilizarlo en un método:

bool IsZero<TNum, T>(TNum ops, T number) 
    where TNum : INumeric<T> 
{ 
    return number == ops.Zero;  
} 
método

o la extensión

public static bool IsZero<TNum, T>(this TNum ops, T number) 
     where TNum : INumeric<T> 
{ 
     return number == ops.Zero; 
} 

y en el código:

... 
var n = new Numeric(); // can be an static prop 

Console.WriteLine(IsZero(n, 5)); // false 
Console.WriteLine(IsZero(n, 0f)); // true 
Console.WriteLine(IsZero(n, "0")); // compiler error 

o, con el método de extensión:

Console.WriteLine(n.IsZero(5)); // false 
Console.WriteLine(n.IsZero(0f)); // true 
Console.WriteLine(n.IsZero("0")); // compiler error 
Cuestiones relacionadas