2008-10-05 11 views
13

cuando intento esto con una clase genérica donde this.value es T:escribiendo valores genéricos (C#)

if (this.value.GetType() == typeof(int)) 
{ 
    ((int)this.value)++; 
} 
else 
{ 
    throw new InvalidOperationException 
      ("T must be an int to perform this operation"); 
} 

me sale un error en tiempo de compilación: "No se puede convertir tipo 'T' a 'int' "

¿Qué debo hacer para realizar una operación integral en this.value cuando se trata de un int?

Tenga en cuenta que esto es solo un ejemplo. El código no escribe conversiones con genéricos, y "int" es solo un ejemplo de un tipo para T.

Respuesta

24

Desafortunadamente, es muy difícil convencer al compilador sobre implementaciones T específicas. Uno de los enfoques (desagradable) es echar a oponerse en el medio (tenga en cuenta que esto caja y tipos de valor Unbox):

int i = (int)(object)this.value; 
i++; 
this.value = (T)(object)i; 

feo, pero funciona. En .NET 3.5 tengo algunos mejores contenedores para la aritmética genérica, here. La clase Operator es parte de MiscUtil; Al nivel más simple, sospecho AddAlternative funcionaría muy bien:

this.value = Operator.AddAlternative(this.value, 1); 

Esto debe inferir la implícita < T, int > automáticamente, o se les puede añadir a sí mismo:

this.value = Operator.AddAlternative<T,int>(this.value, 1); 

Beneficio: Esto es preferible al código original, ya que en realidad no le importa la T original; funcionará para cualquier tipo (incluso el suyo) que admita "T + (T, int)".

Creo que también es un changetype escondido en algún lugar allí ...

[editar] Collin K y otros hacen una observación válida acerca de las implicaciones arquitectónicas - pero siendo pragmática hay momentos en que el T realmente hace Importa mucho ... pero estaría de acuerdo con evitar este tipo de especialización a menos que realmente sea necesario. Dicho esto (según mi comentario sobre la publicación de Collin), la capacidad de realizar cosas como la aritmética básica (incremento, división Int32, etc.) en (por ejemplo) una Matriz <T> [para T en decimal/float/int/doble/etc] a menudo es muy valioso.

3

La tipificación estática de C# no le permitirá hacerlo, pero puede engañar con la fundición al objeto. No recomendaría hacer esto, probablemente muestre un problema de arquitectura, pero de todos modos:

using System; 

class Foo<T> 
{ 
    public T value; 

    public void Increment() 
    { 
    if (value is int) value = (T)(object)(((int)(object)value)+1); 
    } 
} 

static class Program 
{ 
    static void Main() 
    { 
    Foo<int> x = new Foo<int>(); 
    x.Increment(); 
    x.Increment(); 
     Console.WriteLine(x.value); 
    }  
} 
0

Creo que no entiendo lo que está buscando. Si está requiriendo que algo sea de un tipo específico, entonces probablemente no debería usar Genéricos. Podrías, parece tonto. Esto hará lo que está pidiendo, pero no lo recomiendo.

namespace GenericsOne 
{ 
    using System; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Sample<int> one = new Sample<int>(); 
     one.AddFive(10); 

     // yes, this will fail, it is to show why the approach is generally not a good one. 
     Sample<DateTime> two = new Sample<DateTime>(); 
     two.AddFive(new DateTime()); 
    } 
} 

} 

namespace GenericsOne 
{ 
    using System; 
public class Sample<T> 
{ 
    public int AddFive(T number) 
    { 
     int junk = 0; 

     try 
     { 
      junk = Convert.ToInt32(number); 
     } 
     catch (Exception) 
     { 
      Console.WriteLine("Nope"); 
     } 

     return junk + 5; 
    } 
} 
} 
+0

Esto no funciona, el compilador no puede determinar qué sobrecarga ToInt32 usar. – AnthonyWJones

+0

Sí, funciona bien. Pégalo en VS y pruébalo. – user25306

12

En mi opinión, el código de tipo específico en una clase genérica es un olor a código. Me refactorearlo para obtener algo como esto:

public class MyClass<T> 
{ 
... 
} 

public class IntClass : MyClass<int> 
{ 
    public void IncrementMe() 
    { 
    this.value++; 
    } 
} 
+2

Quizás, pero en algunos casos un incremento simple o un divisor Int32 es muy común en el código matemático genérico (es decir, algo así como Matrix donde T es float/double/decimal/int/etc). Pero en muchos casos, sí: algo está maduro. –

0

Esta es la razón por lo que realmente quiero limitaciones numéricas o de operación in C#4.

@Marc La respuesta de Gravell es la mejor manera de evitar esto (+1), pero está frustrando la forma en que esto es un problema para los genéricos.