2009-08-28 10 views
21

Duplicar posible:
C# generic constraint for only integers¿Existe una restricción genérica de C# para los tipos de "números reales"?

saluda!

Estoy intentando configurar un sistema de coordenadas cartesianas en C#, pero no quiero restringirme a ningún tipo numérico para mis valores de coordenadas. A veces pueden ser enteros, y otras veces pueden ser números racionales, según el contexto.

Esto me grita "clase genérica", pero estoy perplejo en cuanto a cómo restringir el tipo a integrales y puntos flotantes. Me parece que no puede encontrar una clase que cubre cualquier concepto de los números reales ...

public class Point<T> where T : [SomeClassThatIncludesBothIntsandFloats?] { 
    T myX, myY; 

    public Point(T x, T y) { 
     myX = x; 
     myY = y; 
    } 
} 

Point<int> pInt = new Point<int>(5, -10); 
Point<float> pFloat = new Point<float>(3.14159, -0.2357); 

Si quiero que este nivel de libertad, estoy eligiendo a un "typeof (T)" pesadilla cuando se trata de cálculos dentro de mis clases, eliminando bools, cadenas, objetos, etc. O peor, ¿elijo hacer una clase para cada tipo de número con el que quiero trabajar, cada uno con las mismas fórmulas matemáticas internas?

Cualquier ayuda sería apreciada. ¡Gracias!

+10

Esta es una característica solicitada con bastante frecuencia. Lo consideramos como una posibilidad de una futura publicación hipotética del compilador/tiempo de ejecución, pero no ocupa un lugar destacado en la lista de prioridades, por lo que no interpretaría esto como una promesa. Recuerde, tenemos CIENTOS de posibles características y cualquier versión dada solo recibe un puñado de ellas. Sin embargo, ciertamente está en nuestro radar. –

+1

Eso es bueno saber, Eric. Soy muy nuevo en C#, pero tengo raíces en Java, así que es como saltar dimensiones ... similar, pero lo suficientemente diferente como para hacerte ir "¿eh?" de vez en cuando. ;) – Syndog

+3

Para todos los demás, muchas gracias por todas sus respuestas. Ustedes han terminado esto! No es de extrañar que cada vez que Google una pregunta C#, stackoverflow.com ocupa un lugar tan destacado en los resultados. – Syndog

Respuesta

16

No puede definir dicha restricción, pero puede verificar el tipo en tiempo de ejecución. Sin embargo, eso no te ayudará a hacer cálculos.

Si usted quiere hacer cálculos, algo como esto sería una opción:

class Calculations<T, S> where S: Calculator<T>, new() 
{ 
    Calculator<T> _calculator = new S(); 

    public T Square(T a) 
    { 
     return _calculator.Multiply(a, a); 
    } 

} 

abstract class Calculator<T> 
{ 
    public abstract T Multiply(T a, T b); 
} 

class IntCalculator : Calculator<int> 
{ 
    public override int Multiply(int a, int b) 
    { 
     return a * b; 
    } 
} 

Del mismo modo, definen un FloatCalculator y cualquier operación que necesita. No es particularmente rápido, aunque es más rápido que el constructo C# 4.0 dynamic.

var calc = new Calculations<int, IntCalculator>(); 
var result = calc.Square(10); 

Un efecto secundario es que sólo será capaz de crear una instancia Calculator si el tipo se pasa a ella tiene una Calculator<T> aplicación a juego, por lo que no tiene que hacer la comprobación de tipos en tiempo de ejecución.

Esto es básicamente a lo que se refería Hejlsberg en this interview donde se trata el problema. Personalmente, me gustaría ver algún tipo de tipo de base :)

+1

Correcto, eso tiene sentido. Básicamente, envuelva mis tipos "primitivos" en clases que implementen una interfaz común, luego restrinja en función de eso. Gracias, Thor! – Syndog

+0

Tenga en cuenta que en .NET 3.5 todavía hay cosas que puede hacer con genéricos: consulte mi respuesta para obtener más información. –

5

Este es un problema conocido, ya que ninguna de las clases aritméticas llegan de la misma clase. Entonces no puedes restringirlo.

La única cosa que podría hacer es

where T : struct 

pero eso no es exactamente lo que quiere.

Aquí hay un enlace al problema específico.

Arithmetic types like int,double,decimal should implement IArithmetic<T>

+0

Hola Stan, solo quería que supieras que eso ayuda bastante. En un solo trazo, hacer que T herede de struct elimina la necesidad de una verificación en tiempo de ejecución para todos los tipos de valores. ¡Gracias amigo! – Syndog

+2

Su enlace da como resultado "página no encontrada". – weberc2

0

Thi s podrían ser útiles. Tienes que usar una clase genérica para lograr lo que quieres.

0

C# no permite actualmente restricciones de tipo en los tipos de valores. Hice una pregunta relacionada no hace mucho tiempo.

Enum type constraints in C#

0

¿Esto se presta a tener clases separadas de ejecución IPOINT?

Algo así como:

public interface IPoint<T> 
{ 
    T X { get; set; } 
    T Y { get; set; } 
} 

public class IntegerPoint : IPoint<int> 
{ 

    public int X { get; set; } 
    public int Y { get; set; } 
} 

Como los cálculos tendrá que ser diferente en cada aplicación de todas formas ¿verdad?

Dan #

+2

Bueno, los cálculos son en realidad idénticos, independientemente de si están implementados utilizando valores enteros o de coma flotante. De lo contrario, me gustaría hacer dos clases separadas que implementen sus respectivos tipos sin ningún genérico o herencia en absoluto. Pero dado que las fórmulas son idénticas, no quiero duplicar el código entre clases. ¡Gracias por su respuesta! – Syndog

3

En realidad se puede hacer esto, aunque la solución es tedioso para configurar, y puede ser confuso para los desarrolladores que no son conscientes de por qué se ha hecho. (¡Así que si elige documentarlo exhaustivamente!) ...

Crea dos estructuras, llamadas say, MyInt y MyDecimal que actúan como fachadas para CTS Int32 y Decimal core types (Contienen un campo interno de ese tipo respectivo.) cada uno debe tener un ctor que toma una instancia del tipo de núcleo CTS como parámetro de entrada ..

haga que cada uno implementar una interfaz vacío llamado INumeric

Luego, en sus métodos genéricos, hacer la restricción basada en esta interfaz. A la baja, en todos los sitios donde desee utilizar estos métodos, debe construir una instancia del tipo personalizado apropiado en lugar del tipo Core CTS y pasar el tipo personalizado al método.

NOTA: la codificación de las estructuras personalizadas para emular correctamente todo el comportamiento de los tipos de núcleo CTS es la parte tediosa ... Debe implementar varias interfaces CLR incorporadas (IComparable, etc.) y sobrecargar toda la aritmética, y operadores booleanos ...

12

Esta es una pregunta muy común; si está utilizando .NET 3.5, hay una gran cantidad de soporte para esto en MiscUtil, a través de la clase Operator, que admite tipos incorporados y cualquier tipo personalizado con operadores (incluidos los operadores "levantados"); En particular, esto permite el uso con genéricos, por ejemplo:

public static T Sum<T>(this IEnumerable<T> source) { 
    T sum = Operator<T>.Zero; 
    foreach (T value in source) { 
     if (value != null) { 
      sum = Operator.Add(sum, value); 
     } 
    } 
    return sum; 
} 

o para otro ejemplo; Complex<T>

+0

Interesante. Parece que usa la funcionalidad del árbol de expresiones LINQ, pero evalúa inmediatamente cada bit? – Thorarin

+0

Internamente, utiliza un árbol de expresiones para precompilar (una vez solo por tipo) un delegado que puede hacer la aritmética. –

1

Usted puede estar más cerca de la aplicación de unos cuantos más

public class Point<T> where T : struct, IComparable, IFormattable, IConvertible, 
           IComparable<T>, IEquatable<T> { 
} 

La firma se ajusta a DateTime también. No estoy seguro de si podrá especificar más tipos del marco. De todos modos, esto solo resuelve parte del problema. Para realizar operaciones numéricas básicas, deberá ajustar sus tipos numéricos y usar métodos genéricos en lugar de los operadores estándar. See this SO question para algunas opciones.

Cuestiones relacionadas