web-dev-qa-db-fra.com

Lancer un objet en T

J'analyse un fichier XML avec la classe XmlReader dans .NET et je pensais qu'il serait intelligent d'écrire une fonction d'analyse générique pour lire différents attributs de manière générique. Je suis venu avec la fonction suivante:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

Comme je me suis rendu compte, cela ne fonctionne pas entièrement comme je l'avais prévu; il génère une erreur avec les types primitifs tels que int ou double, car un transtypage ne peut pas convertir d'un string en un type numérique. Existe-t-il un moyen de faire prévaloir ma fonction sous une forme modifiée?

75
Kasper Holdum

Vérifiez d'abord si elle peut être lancée.

if (readData is T) {
    return (T)readData;
} 
try {
   return (T)Convert.ChangeType(readData, typeof(T));
} 
catch (InvalidCastException) {
   return default(T);
}
182
Bob

Avez-vous essayé Convert.ChangeType ?

Si la méthode renvoie toujours une chaîne, ce que je trouve étrange, mais c'est en plus du point, alors peut-être que ce code modifié ferait ce que vous voulez:

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)Convert.ChangeType(readData, typeof(T));
}

essayer

if (readData is T)
    return (T)(object)readData;
7
Sadegh

Vous pouvez exiger que le type soit un type de référence:

 private static T ReadData<T>(XmlReader reader, string value) where T : class
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     return (T)readData;
 }

Et puis faites un autre qui utilise des types de valeur et TryParse ...

 private static T ReadDataV<T>(XmlReader reader, string value) where T : struct
 {
     reader.MoveToAttribute(value);
     object readData = reader.ReadContentAsObject();
     int outInt;
     if(int.TryParse(readData, out outInt))
        return outInt
     //...
 }
3
Tom Ritter

En fait, le problème ici est l'utilisation de ReadContentAsObject. Malheureusement, cette méthode ne répond pas à ses attentes; bien qu'il devrait détecter le type le plus approprié pour la valeur, il retourne en fait une chaîne, quoi qu'il en soit (cela peut être vérifié à l'aide de Reflector).

Cependant, dans votre cas spécifique, vous connaissez déjà le type vers lequel vous souhaitez caster, donc je dirais que vous utilisez la mauvaise méthode.

Essayez plutôt d'utiliser ReadContentAs, c'est exactement ce dont vous avez besoin.

private static T ReadData<T>(XmlReader reader, string value)
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAs(typeof(T), null);
    return (T)readData;
}
3
baretta

Vous pouvez vraisemblablement transmettre, en tant que paramètre, un délégué qui convertira la chaîne en T.

2
ChrisW

Ajoutez une contrainte de "classe" (ou plus détaillée, comme une classe de base ou une interface de vos objets T attendus):

private static T ReadData<T>(XmlReader reader, string value) where T : class
{
    reader.MoveToAttribute(value);
    object readData = reader.ReadContentAsObject();
    return (T)readData;
}

ou where T : IMyInterface ou where T : new(), etc.

1
Ricardo Villamil

En fait, les réponses soulèvent une question intéressante, qui est ce que vous voulez que votre fonction fasse en cas d'erreur.

Peut-être qu'il serait plus logique de le construire sous la forme d'une méthode TryParse qui tente de lire dans T, mais renvoie false si cela ne peut pas être fait?

    private static bool ReadData<T>(XmlReader reader, string value, out T data)
    {
        bool result = false;
        try
        {
            reader.MoveToAttribute(value);
            object readData = reader.ReadContentAsObject();
            data = readData as T;
            if (data == null)
            {
                // see if we can convert to the requested type
                data = (T)Convert.ChangeType(readData, typeof(T));
            }
            result = (data != null);
        }
        catch (InvalidCastException) { }
        catch (Exception ex)
        {
            // add in any other exception handling here, invalid xml or whatnot
        }
        // make sure data is set to a default value
        data = (result) ? data : default(T);
        return result;
    }

edit: maintenant que j'y pense, ai-je vraiment besoin de faire le test convert.changetype? la ligne as n'essaye-t-elle pas déjà de le faire? Je ne suis pas sûr que faire cet appel de type de changement supplémentaire accomplisse réellement quelque chose. En fait, cela pourrait simplement augmenter la surcharge de traitement en générant une exception. Si quelqu'un connaît une différence qui en vaut la peine, veuillez poster!

1
genki