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

90

Puedo llamar Get<int>(Stat);oGet<string>(Name);

Pero al compilar obtengo:

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;
    }
}
David W
fuente
6
Probablemente esté pensando que el bloque if verificó que T es int, por lo que dentro del bloque, sabe que T es int y debería poder convertir implícitamente int a T.Pero el compilador no está diseñado para seguir ese razonamiento, solo sabe que generalmente T no se deriva de int, por lo que no permite la conversión implícita. (Y si el compilador lo
admitió

Respuestas:

132

Cada vez que se encuentra encendiendo un tipo en un genérico , es casi seguro que está haciendo algo mal . Los genéricos deben ser genéricos ; deben operar de manera idéntica completamente independiente del tipo .

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

Eric Lippert
fuente
1
Obtenga <Car> donde el automóvil implemente IConvertible causará roturas. Cuando alguien ve que tiene un método genérico, asumirá que puede pasar cualquier cosa que implemente IConvertible.
Tjaart
10
Solo puedo estar de acuerdo parcialmente contigo, @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 ser no solo float, int y bool sino también algunos tipos personalizados. Sin embargo, en caso de que obtenga un float [] (las etiquetas de matriz contienen el tipo de datos almacenados en sus nombres: float_array almacena flotantes), debe analizar la cadena como una matriz de floats, que requiere que se use algún IFormatProvider). Obviamente no puedo usar "T.Parse (...)". Entonces, para un pequeño subconjunto de casos, necesito usar dicha conmutación.
rbaleksandar
1
Esta respuesta te mantendrá fuera de la madriguera del conejo. Quería hacer una función genérica para int, int?, bool, bool?, string, y aparentemente era imposible.
Jess
Esto hace que sea práctico cambiar a un tipo enumerado genérico.
David A. Gray
1
No quería usar esto como respuesta. Pero tiene razón. Quería verificar el tipo y, si era específico, establecer una propiedad en él. La solución fue crear un método que tomara un parámetro fuertemente tipado.
Matt Dawdy
141

Debería poder usar en Convert.ChangeType()lugar de su código personalizado:

public T Get<T>(Stats type) where T : IConvertible
{
    return (T) Convert.ChangeType(PlayerStats[type], typeof(T));
}
Vidrio roto
fuente
21
¿Qué talreturn (T)(object)PlayerStats[type];
maxp
11
public T Get<T>(Stats type ) where T : IConvertible
{
    if (typeof(T) == typeof(int))
    {
        int t = Convert.ToInt16(PlayerStats[type]);
        return (T)t;
    }
    if (typeof(T) == typeof(string))
    {
        string t = PlayerStats[type].ToString();
        return (T)t;
    }
}
Reza ArabQaeni
fuente
2
return (T) t;porque no son necesarias comprobaciones nulas.
BoltClock
Esto de arriba no se compilará para mí. T debe ser un tipo de referencia para la compilación de "as".
Robert Schmidt
9

ChangeTypees probablemente tu 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;
}
Michael Ciba
fuente
Mi caso de uso es una clase concreta derivada de una clase abstracta genérica. La clase se marca como abstracta porque define un método abstracto que opera en el miembro privado genérico de la clase base. El genérico usa la restricción Enum de C # 7.3 en su tipo genérico. Acabo de completar con éxito una prueba y funciona exactamente como esperaba.
David A. Gray
8

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];
    }
}
Michael Kalinovich
fuente
Gracias, esto ayudó, mi necesidad es diferente. Estoy escribiendo un método simulado para un método estático existente para poder probarlo. Usando este osherove.com/blog/2012/7/8/…
Esen
8

En realidad, puede convertirlo a objecty 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 según el Ttipo.

Gregor Mohorko
fuente
6

Teniendo en cuenta que @BrokenGlass, la lógica ( Convert.ChangeType) no es compatible con el tipo GUID.

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

Error : conversión no válida de 'System.String' a 'System.Guid'.

En su lugar, use la siguiente lógica usando TypeDescriptor.GetConverteragregando System.ComponentModelespacio de nombres.

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

Lea esto .

Prasad Kanaparthi
fuente
0

Simplemente puede lanzar como a continuación,

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;
 }
}
Vijayanath Viswanathan
fuente