Impossibile convertire implicitamente il tipo "Int" in "T"


92

Posso chiamare Get<int>(Stat);oGet<string>(Name);

Ma durante la compilazione ottengo:

Impossibile convertire implicitamente il tipo "int" in "T"

e la stessa cosa per 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;
    }
}

6
Probabilmente stai pensando che il blocco if ha verificato che T è int, quindi all'interno del blocco, sai che T è int e dovresti essere in grado di convertire implicitamente int in T. Ma il compilatore non è progettato per seguire quel ragionamento, lo sa e basta che generalmente T non deriva da int, quindi non consente la conversione implicita. (E se il compilatore lo supportasse, il verificatore no, quindi l'assembly compilato non sarebbe verificabile.)
JGWeissman

Risposte:


132

Ogni volta che ti ritrovi ad attivare un tipo in un generico , quasi sicuramente stai facendo qualcosa di sbagliato . I generici dovrebbero essere generici ; dovrebbero funzionare in modo identico e completamente indipendente dal tipo .

Se T può essere solo int o string, in primo luogo non scrivere il codice in questo modo. Scrivi due metodi, uno che restituisce un int e uno che restituisce una stringa.


1
Prendi <Auto> dove la macchina implementi IConvertible provocherà rotture. Quando qualcuno vede che hai un metodo generico, presumerà di poter passare qualsiasi cosa che implementi IConvertible.
Tjaart,

11
Posso solo parzialmente essere d'accordo con te, @Eric .Ho una situazione in cui devo analizzare gli array memorizzati nei tag XML.Il problema è che la specifica che segue il documento XML (COLLADA nel mio caso) dice che tali array possono essere non solo float, int e bool ma anche alcuni tipi personalizzati. Tuttavia, nel caso in cui si ottenga un float [] (i tag array contengono il tipo di dati memorizzati nei loro nomi: float_array memorizza i float) è necessario analizzare la stringa come un array di floats, che richiede l'uso di IFormatProvider). Ovviamente non posso usare "T.Parse (...)" Quindi per un piccolo sottoinsieme di casi ho bisogno di usare tale commutazione.
rbaleksandar

1
Questa risposta ti terrà fuori dalla tana del coniglio. Volevo creare una funzione generica per int, int?, bool, bool?, string, ed era apparentemente impossibile.
Jess

Ciò rende pratica un'opzione su un tipo enumerato generico.
David A. Gray

1
Non volevo usare questo come risposta. Ma ha ragione. Volevo controllare il tipo e, se specifico, impostare una proprietà su di esso. La soluzione era creare un metodo che prendesse un parametro fortemente tipizzato.
Matt Dawdy,

143

Dovresti essere in grado di usare solo al Convert.ChangeType()posto del tuo codice personalizzato:

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

22
Che ne dicireturn (T)(object)PlayerStats[type];
maxp

@BrokenGlass Grazie Funziona bene.
ASLIM

@maxp becareful Non funziona per scopi generali, ad esempio il casting di tipo non gestito richiede 'Convert.ChangeType ()'.
ASLIM

10
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;
    }
}

2
return (T) t;perché non sono necessari controlli nulli.
BoltClock

Questo sopra non verrà compilato per me. T deve essere un tipo di riferimento per "as" da compilare.
Robert Schmidt

9

ChangeTypeè probabilmente la tua migliore opzione. La mia soluzione è simile a quella fornita da BrokenGlass con un po 'di logica try catch.

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;
}

Il mio caso d'uso è una classe concreta derivata da una classe astratta generica. La classe è contrassegnata come astratta perché definisce un metodo astratto che opera sul membro privato generico della classe di base. Il generico usa il vincolo C # 7.3 Enum sul suo tipo generico. Ho appena completato con successo un test e funziona esattamente come speravo.
David A. Gray il

8

Prova questo:

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];
    }
}

Grazie per avermi aiutato, il mio bisogno è diverso. Sto scrivendo un metodo fittizio per un metodo statico esistente in modo da poterlo testare. Utilizzando questo osherove.com/blog/2012/7/8/…
Esen

8

In realtà, puoi semplicemente convertirlo in objecte poi in T.

T var = (T)(object)42;

Un esempio per 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 volte, questo comportamento è desiderabile. Ad esempio, quando si implementa o si sovrascrive un metodo generico da una classe o interfaccia di base e si desidera aggiungere alcune funzionalità diverse in base al Ttipo.


6

Considerando che @BrokenGlass logic ( Convert.ChangeType) non supporta il tipo di GUID.

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

Errore : cast non valido da "System.String" a "System.Guid".

Utilizzare invece la logica seguente utilizzando TypeDescriptor.GetConverteraggiungendo lo System.ComponentModelspazio dei nomi.

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

Leggi questo .



0

Puoi semplicemente trasmettere come di seguito,

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;
 }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.