2011-11-17 31 views
58

me puede llamar Get<int>(Stat); o Get<string>(Name);No se puede convertir implícitamente el tipo 'int' a 'T'

Pero al compilar me sale:

No se puede convertir implícitamente el tipo 'int' a 'T'

y lo mismo para string.

public T Get<T>(Stats type) where T : IConvertible 
{ 
    if (typeof(T) == typeof(int)) 
    { 
     int t = Convert.ToInt16(PlayerStats[type]); 
     return t; 
    } 
    if (typeof(T) == typeof(string)) 
    { 
     string t = PlayerStats[type].ToString(); 
     return t; 
    } 
} 
+4

Probablemente esté pensando que el bloque if verificó que T es int, por lo que dentro del bloque, sabe que T es int y debe poder convertir imp ínticamente a T. Pero el compilador no está diseñado para seguir ese razonamiento, simplemente sabe que generalmente T no deriva de int, por lo que no permite la conversión implícita. (Y si el compilador lo soportaba, el verificador no lo haría, por lo que el ensamblado compilado no podría verificarse). – JGWeissman

Respuesta

99

Cada vez que te encuentres conectando un tipo en un genérico casi seguro que estás haciendo algo mal. Los genéricos deben ser genéricos; deberían funcionar de manera idéntica completamente independiente del tipo.

Si T solo puede ser int o cadena, no escriba su código de esta manera en absoluto en primer lugar. Escriba dos métodos, uno que devuelva un int y otro que devuelva una cadena.

+2

¿Podría profundizar sobre cuándo esto podría salir mal? –

+1

Obtenga donde el automóvil implemente IConvertible causará rotura. Cuando alguien ve que tienes un método genérico, asumirán que pueden pasar cualquier cosa que implemente IConvertible. – Tjaart

+5

Solo puedo estar parcialmente de acuerdo con usted, @Eric. Tengo una situación en la que tengo que analizar matrices almacenadas en etiquetas XML. El problema es que la especificación que sigue el documento XML (COLLADA en mi caso) dice que tales matrices pueden no solo float, int y bool, sino también algunos tipos personalizados. Sin embargo, en caso de que obtengas un float [] (los array-tags contienen el tipo de datos almacenados en sus nombres: float_array stores floats) necesitas analizar el string como un array de flotadores, lo que requiere algún IFormatProvider para ser utilizado). Obviamente no puedo usar "T.Parse (...)". Por lo tanto, para un pequeño subconjunto de casos, necesito usar dicho cambio. – rbaleksandar

9
public T Get<T>(Stats type) where T : IConvertible 
{ 
    if (typeof(T) == typeof(int)) 
    { 
     int t = Convert.ToInt16(PlayerStats[type]); 
     return t as T; 
    } 
    if (typeof(T) == typeof(string)) 
    { 
     string t = PlayerStats[type].ToString(); 
     return t as T; 
    } 
} 
+2

'return (T) t;' porque no se necesitan verificaciones nulas. – BoltClock

+1

si el estado no es nulo –

+0

Ha! +1: tuve un problema diferente, pero el retorno t como T lo resolvió para mí. Gracias. – CodeChops

95

Usted debe ser capaz de usar sólo Convert.ChangeType() en lugar de su código personalizado:

public T Get<T>(Stats type) where T : IConvertible 
{ 
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T)); 
} 
+2

Gracias que funciona perfectamente –

+7

¿Qué tal 'return (T) (object) PlayerStats [type];' – maxp

+0

Gracias por darnos una respuesta. Nunca digas nunca". – mikesigs

6

ChangeType es probablemente su mejor opción. Mi solución es similar a la proporcionada por BrokenGlass con un poco de lógica de captura de prueba.

static void Main(string[] args) 
{ 
    object number = "1"; 
    bool hasConverted; 
    var convertedValue = DoConvert<int>(number, out hasConverted); 

    Console.WriteLine(hasConverted); 
    Console.WriteLine(convertedValue); 
} 

public static TConvertType DoConvert<TConvertType>(object convertValue, out bool hasConverted) 
{ 
    hasConverted = false; 
    var converted = default(TConvertType); 
    try 
    { 
     converted = (TConvertType) 
      Convert.ChangeType(convertValue, typeof(TConvertType)); 
     hasConverted = true; 
    } 
    catch (InvalidCastException) 
    { 
    } 
    catch (ArgumentNullException) 
    { 
    } 
    catch (FormatException) 
    { 
    } 
    catch (OverflowException) 
    { 
    } 

    return converted; 
} 
4

Teniendo en cuenta la lógica @BrokenGlass (Convert.ChangeType) no admite para el tipo GUID.

public T Get<T>(Stats type) where T : IConvertible 
{ 
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T)); 
} 

error: fundido no válida del 'System.String' a 'System.Guid'.

En su lugar, utilice la lógica siguiente utilizando TypeDescriptor.GetConverter agregando System.ComponentModel espacio de nombres.

public T Get<T>(Stats type) where T : IConvertible 
{ 
    (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(PlayerStats[type]) 
} 

Leído this.

5

Prueba esto:

public T Get<T>(Stats type) where T : IConvertible 
{ 
    if (typeof(T) == typeof(int)) 
    { 
     return (T)(object)Convert.ToInt16(PlayerStats[type]); 

    } 
    if (typeof(T) == typeof(string)) 
    { 

     return (T)(object)PlayerStats[type]; 
    } 
} 
+0

Gracias esto ayudó, mi necesidad es diferente. Estoy escribiendo un método simulado para un método estático existente para que pueda probarlo. Usando este http://osherove.com/blog/2012/7/8/faking-static-methods-in-moq-fakeiteasy-and-nsubstitute-migh.html – Esen

0

En realidad, sólo puede convertirlo a object y luego a T.

T var = (T)(object)42;

Un ejemplo para bool:

public class Program 
{ 
    public static T Foo<T>() 
    { 
     if(typeof(T) == typeof(bool)) { 
      return (T)(object)true; 
     } 

     return default(T); 
    } 

    public static void Main() 
    { 
     bool boolValue = Foo<bool>(); // == true 
     string stringValue = Foo<string>(); // == null 
    } 
} 

A veces, este comportamiento es deseable. Por ejemplo, al implementar o anular un método genérico de una clase base o interfaz y desea agregar algunas funcionalidades diferentes basadas en el tipo T.

Cuestiones relacionadas