182 votes

La valeur de type 'T' ne peut pas être convertie en une valeur de type 'T'.

Il s'agit probablement d'une question de novice, mais, étonnamment, Google n'a pas fourni de réponse.

J'ai cette méthode plutôt artificielle

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

    return t;
}

Étant donné que je viens d'un milieu C++, je m'attendais à ce que cela fonctionne. Cependant, la compilation échoue avec "Cannot implicitly convert type 'T' to string" et "Cannot convert type 'T' to string" pour les deux affectations ci-dessus.

Soit je fais quelque chose de mal sur le plan conceptuel, soit je n'ai pas la bonne syntaxe. Aidez-moi à résoudre ce problème.

Merci !

23 votes

IMO, si vous vérifiez les types dans votre code générique, alors les génériques ne sont probablement pas la bonne solution à votre problème.

0 votes

L'expression typeof(T) == typeof(string) est résolu au moment de l'exécution, et non de la compilation. Ainsi, la ligne suivante du bloc n'est pas valide.

9 votes

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

335voto

SLaks Points 391154

Même si c'est à l'intérieur d'un if le compilateur ne sait pas que le bloc T es string .
Par conséquent, il ne vous permet pas de lancer. (Pour la même raison que vous ne pouvez pas lancer DateTime à string )

Vous devez lancer pour object (que tout T peut lancer vers), et de là vers string (depuis object peut être transformé en string ).
Par exemple :

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

4 votes

Ça marche ! Je suppose que le deuxième like devrait aussi être T newT2 = (T)(object)t ; bien que ce soit un no op.

2 votes

Ajout : Les modèles C++ sont essentiellement des copier-coller au moment de la compilation avec les valeurs correctes substituées. En C#, le modèle générique réel (et non son "instanciation") existe après la compilation et doit donc (excusez le jeu de mots) être générique dans les limites du type spécifié.

0 votes

(chaîne de caractères)(objet)t ; ne sert à rien ici, il vaut mieux laisser tomber, la (chaîne de caractères)(objet) qui est

13voto

Doggett Points 1920

Les deux lignes ont le même problème

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

Le compilateur ne sait pas que T est une chaîne et n'a donc aucun moyen de savoir comment l'assigner. Mais puisque vous avez vérifié, vous pouvez simplement le forcer avec

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

vous n'avez pas besoin de caster le t puisqu'il s'agit déjà d'une chaîne de caractères, il faut également ajouter la contrainte

where T : class

2 votes

Faux. Cela ne compilera pas. Voir ma réponse.

2 votes

Compile très bien (avec le où qui est, ajouté quelques secondes après avoir posté, j'aurais pu le manquer). Oops nm ai oublié de changer le casting

7voto

feO2x Points 1415

Je connais un code similaire à celui que le PO a posté dans cette question à partir d'analyseurs génériques. Du point de vue des performances, vous devriez utiliser Unsafe.As<TFrom, TResult>(ref TFrom source) qui se trouve dans le System.Runtime.CompilerServices.Unsafe Paquet NuGet. Cela permet d'éviter la mise en boîte des types de valeurs dans ces scénarios. Je pense également que Unsafe.As donne lieu à moins de code machine produit par le JIT qu'une double coulée (en utilisant (TResult) (object) actualString ), mais je ne l'ai pas vérifié.

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 sera remplacé par le JIT avec des instructions efficaces en code machine, comme vous pouvez le voir dans le repo officiel de CoreFX :

Source Code of Unsafe.As

1voto

Austin Salonen Points 28057

Si vous vérifiez les types explicites, pourquoi déclarez-vous ces variables en tant que 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 votes

La deuxième ligne crée une variable de type T .

0 votes

Vous demandez pourquoi vérifier le type ? Supposons que vous ayez un type de champ de base qui enregistre object avec des types dérivés qui stockent string valeurs. Supposons que ces champs aient également une valeur "DefaultIfNotProvided". Vous devez donc vérifier si la valeur fournie par l'utilisateur (qui peut être un objet, une chaîne ou même une primitive numérique) est équivalente à default(T) . String peut être traité comme un cas spécial où une chaîne vide/avec espace est traitée de la même manière que default(T), donc vous pouvez vérifier si T userValue; var isBlank = (userValue is string) && String.IsNullOrWhitespace(userValue as string); .

-1voto

John Points 1

Vous obtiendrez également cette erreur si vous avez une déclaration générique à la fois pour votre classe et votre méthode. Par exemple, le code ci-dessous donne cette erreur de compilation.

public class Foo <T> {

    T var;

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

}

Ce code compile (notez que le T a été retiré de la déclaration de la méthode) :

public class Foo <T> {

    T var;

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

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X