2012-02-18 21 views
5

Duplicar posible:
C# generic constraint for only integers¿Cómo sumar números genéricos en C#?

Como se puede ver en el siguiente código, necesito para calcular la suma de dos números genéricos.

public class NumberContainer<T> 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public T Total { get { return ValueA + ValueB; } } 
} 

Sin embargo, no es posible hacer una adición directa de los dos valores de T, que resulta en el error de compilador a continuación: no se puede aplicar a operandos de tipo

operador '+' 'T' y 'T'

Dado que no tengo la intención de usar T para nada más que tipos de valores que representan números (short, ushort, int, uint, etc.), ¿cómo podría realizar el ¿suma? (la eficiencia es un factor a considerar)

+0

O posiblemente: [¿Hay una limitación genérica de C# para el “número real” tipos?] (http://stackoverflow.com/questions/1348594/is-there-ac-sharp-generic-constraint-for-real-number-types/1348625#1348625) –

+1

La pregunta está cerrada como un duplicado de preguntas buscando poner una restricción en 'T', que * no * es lo que esta pregunta está tratando de hacer. Creo que debería ser reabierto. – dasblinkenlight

Respuesta

12

Puede hacerlo con "poco de magia" de LINQ:

private static readonly Func<T, T, T> adder; 
static NumberContainer() { 
    var p1 = Expression.Parameter(typeof (T)); 
    var p2 = Expression.Parameter(typeof (T)); 
    adder = (Func<T, T, T>)Expression 
     .Lambda(Expression.Add(p1, p2), p1, p2) 
     .Compile(); 
} 
public T Total { get { return adder(ValueA, ValueB); } } 

El único inconveniente es que este código se compilar incluso si NumberContainer se crea una instancia de un tipo T que no admite la adición; por supuesto lanzará una excepción en tiempo de ejecución. Una ventaja adicional es que este debería funcionar con operadores definidos por el usuario +.

1

Puede especificar una restricción para T que debe ser una estructura, y quizás que implementa IConvertable, y luego use Convert.

public class NumberContainer<T> 
     where T : struct, IConvertible 
    { 
     public T ValueA { get; private set; } 
     public T ValueB { get; private set; } 
     public T Total { 
      get 
      { 
       // do type checking here, then: 

       return (T)Convert.ChangeType(
        Convert.ToDouble((object)ValueA) + 
        Convert.ToDouble((object)ValueB), typeof(T)); 
      } 
     } 
    } 

Sin embargo, no hay ninguna manera con los genéricos para garantizar que T es un entero o de tipo flotante en tiempo de compilación. Sin embargo, puedes verificarlo en tiempo de ejecución y lanzar una excepción.

+0

Esto tampoco funcionará. De hecho, tu sugerencia ni siquiera compila. –

+0

Lo saqué de la parte superior de mi cabeza, lo arreglé y compilé. Si desea profundizar sobre por qué no va a funcionar, sería mucho más útil. – HackedByChinese

+0

DateTime es una estructura y un IConvertible pero no se puede convertir en doble, por lo que se lanzará una excepción cada vez que se llame con los parámetros DateTime. –

5

Además de utilizar LINQ, si estás en .NET 4 esto es posible usando dynamic:

static T Add<T>(T x, T y) 
{ 
    dynamic dx = x, dy = y; 
    return dx + dy; 
} 

En el ejemplo de código esto se convierte en:

public class NumberContainer<T> where T: struct 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public T Total { get { return ((dynamic)ValueA) + ((dynamic)ValueB); } } 
} 

Este enfoque doesn' t garantizar la seguridad sin embargo. Si T es una estructura aleatoria que no admite +, terminará con una excepción (creo).

+0

Uso escalofriante de dinámica, especialmente considerando que los operadores no son virtuales ... Supongo que "solo funcionará" con su caja mágica. (Hubiera preferido los tipos estructurales * también *: - /) –

+0

@pst - De acuerdo, pero funciona. Entiendo el punto sin embargo. –

1

Se podría tratar esto sólo como una clase base abstracta y derivar clases específicas que especifican el valor T y proporcionaron la aplicación para Total

public abstract class NumberContainer<T> 
    where T: struct 
{ 
    public T ValueA { get; private set; } 
    public T ValueB { get; private set; } 
    public abstract T Total(); 
} 

public class IntContainer : NumberContainer<int> 
{ 
    public override int Total() 
    { 
     return ValueA + ValueB; 
    } 
} 
+1

Si un 'int' se puede convertir a' T', entonces esto no sería un problema. Tu código no se compila. –

+0

tonto yo. Respuesta actualizada para proporcionar una solución alternativa ya que ahora hay algunas soluciones que usan dinámica y enlace –