Il valore di tipo 'T' non può essere convertito in


146

Questa è probabilmente una domanda per principianti, ma sorprendentemente Google non ha fornito una risposta.

Ho questo metodo piuttosto artificiale

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        T newT1 = "some text";
        T newT2 = (string)t;
    }

    return t;
}

Proveniente da un background C ++, mi aspettavo che funzionasse. Tuttavia, non riesce a compilare con "Impossibile convertire implicitamente il tipo 'T' in stringa" e "Impossibile convertire il tipo 'T' in stringa" per entrambe le assegnazioni precedenti.

O sto facendo qualcosa di concettualmente sbagliato o ho solo la sintassi sbagliata. Per favore, aiutami a risolvere questo.

Grazie!


20
IMO, se stai controllando i tipi nel tuo codice generici, probabilmente i generici non sono la soluzione corretta al tuo problema.
Austin Salonen,

L'espressione typeof(T) == typeof(string)viene risolta in fase di esecuzione, non in fase di compilazione. Pertanto la seguente riga nel blocco non è valida.
Steve Guidi,

8
(T) Convert.ChangeType (newT1, typeof (T))
vsapiha

2
@vsapiha, Funziona solo se l'oggetto implementa IConvertible. Dolcezza se lo fa però.
ouflak,

Risposte:


285

Anche se è all'interno di un ifblocco, il compilatore non sa che Tè string.
Pertanto, non ti consente di lanciare. (Per la stessa ragione per cui non puoi lanciare DateTimea string)

Devi lanciare object, (a cui chiunque Tpuò lanciare), e da lì a string(dato che objectpuò essere lanciato a string).
Per esempio:

T newT1 = (T)(object)"some text";
string newT2 = (string)(object)t;

2
Questo funziona! Immagino che il secondo like dovrebbe essere anche T newT2 = (T) (oggetto) t; anche se questo è un no op.
Alex,

2
Aggiunta: i modelli C ++ sono essenzialmente taglia e incolla al momento della compilazione con i valori corretti sostituiti. In C # il modello generico effettivo (non una "istanza" di esso esiste dopo la compilazione e quindi deve (scusate il gioco di parole) essere generico attraverso i limiti di tipo specificati.

(stringa) (oggetto) t; non fa nulla qui, potrebbe anche lasciarlo fuori, la (stringa) (oggetto) che è
Doggett,

6
Perché non semplicemente lanciare usando "as string"? Ad esempio, questo si compila bene (l'ho letteralmente appena compilato senza errori) quando userDefinedValue è di tipo T:var isBlank = (userDefinedValue is string) && String.IsNullOrWhiteSpace(userDefinedValue as string);
Triynko

1
Questo sembra un errore da parte dei progettisti del compilatore. Se tutti i T possono essere lanciati in modo esplicito su oggetto e tutti gli oggetti possono essere lanciati in modo esplicito su stringa, allora dovrebbe esistere una regola transitiva che T può essere esplicitamente lanciato su stringa. Se esegui il cast in modo errato, dovrebbe verificarsi un errore di runime.
P.Brian.Mackey,

10

Entrambe le linee hanno lo stesso problema

T newT1 = "some text";
T newT2 = (string)t;

Il compilatore non sa che T è una stringa e quindi non ha modo di saperlo assegnare. Ma dal momento che hai controllato puoi semplicemente forzarlo

T newT1 = "some text" as T;
T newT2 = t; 

non è necessario eseguire il cast di t poiché è già una stringa, inoltre è necessario aggiungere il vincolo

where T : class

2
Sbagliato. Questo non verrà compilato. Vedi la mia risposta
SLaks

2
Compilare bene (con dove si trova, aggiunto che pochi secondi dopo che ho pubblicato, potrebbe essermi perso). Oops nm ha dimenticato di cambiare il cast
Doggett il

2

Conosco un codice simile che l'OP ha pubblicato in questa domanda da parser generici. Dal punto di vista delle prestazioni, è necessario utilizzare Unsafe.As<TFrom, TResult>(ref TFrom source), disponibile nel pacchetto NuGet System.Runtime.CompilerServices.Unsafe . Evita la boxe per i tipi di valore in questi scenari. Penso anche che si Unsafe.Astraduca in meno codice macchina prodotto dalla JIT rispetto al casting due volte (utilizzo (TResult) (object) actualString), ma non l'ho verificato.

public TResult ParseSomething<TResult>(ParseContext context)
{
    if (typeof(TResult) == typeof(string))
    {
        var token = context.ParseNextToken();
        string parsedString = token.ParseToDotnetString();
        return Unsafe.As<string, TResult>(ref parsedString);
    }
    else if (typeof(TResult) == typeof(int))
    {
        var token = context.ParseNextToken();
        int parsedInt32 = token.ParseToDotnetInt32();
        // This will not box which might be critical to performance
        return Unsafe.As<int, TResult>(ref parsedInt32); 
    }
    // other cases omitted for brevity's sake
}

Unsafe.As verrà sostituito da JIT con istruzioni efficienti sul codice macchina, come puoi vedere nel repository CoreFX ufficiale:

Codice sorgente di Unsafe.As


1

Se stai cercando tipi espliciti, perché stai dichiarando quelle variabili come T's?

T HowToCast<T>(T t)
{
    if (typeof(T) == typeof(string))
    {
        var newT1 = "some text";
        var newT2 = t;  //this builds but I'm not sure what it does under the hood.
        var newT3 = t.ToString();  //for sure the string you want.
    }

    return t;
}

6
La seconda riga crea una variabile di tipo T.
SLaks

Ti chiedi perché controllare il tipo? Supponiamo di avere un tipo di campo base che memorizza objectvalori, con tipi derivati ​​che memorizzano stringvalori. Supponiamo che questi campi abbiano anche un valore "DefaultIfNotProvided", quindi è necessario verificare se il valore fornito dall'utente (che potrebbe essere un oggetto o una stringa o persino una primitiva numerica) è equivalente default(T). La stringa può essere trattata come un caso speciale in cui una stringa vuota / vuota è trattata come quella predefinita (T), quindi potresti voler verificare se T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string);.
Triynko,

0

Riceverai anche questo errore se hai una dichiarazione generica sia per la tua classe che per il tuo metodo. Ad esempio, il codice mostrato di seguito fornisce questo errore di compilazione.

public class Foo <T> {

    T var;

    public <T> void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

Questo codice viene compilato (nota T rimossa dalla dichiarazione del metodo):

public class Foo <T> {

    T var;

    public void doSomething(Class <T> cls) throws InstantiationException, IllegalAccessException {
        this.var = cls.newInstance();
    }

}

-5

Cambia questa riga:

if (typeof(T) == typeof(string))

Per questa linea:

if (t.GetType() == typeof(string))

1
sono gli stessi
bigworld12

Entrambi sono uguali ... solo usando la parola chiave language vs usando le API della libreria di classi.
Abdulhameed,
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.