Conversion de type générique FROM chaîne


234

J'ai une classe que je veux utiliser pour stocker des "propriétés" pour une autre classe. Ces propriétés ont simplement un nom et une valeur. Idéalement, ce que j'aimerais, c'est pouvoir ajouter des propriétés typées , de sorte que la "valeur" retournée soit toujours du type que je veux qu'elle soit.

Le type doit toujours être une primitive. Cette classe sous-classe une classe abstraite qui stocke essentiellement le nom et la valeur sous forme de chaîne. L'idée étant que cette sous-classe va ajouter une certaine sécurité de type à la classe de base (ainsi que me sauver sur une conversion).

J'ai donc créé une classe qui est (à peu près) la suivante:

public class TypedProperty<DataType> : Property
{
    public DataType TypedValue
    {
        get { // Having problems here! }
        set { base.Value = value.ToString();}
    }
}

La question est donc:

Existe-t-il un moyen "générique" de reconvertir une chaîne en une primitive?

Je n'arrive pas à trouver d'interface générique qui relie la conversion à tous les niveaux (quelque chose comme ITryParsable aurait été idéal!).


Je serais intéressé de voir un exemple de votre classe concrète, même juste un extrait. :)
Jon Limjap

pouvez-vous s'il vous plaît poster les parties pertinentes de votre classe de base?
JJS

Je me demande si quelqu'un peut obtenir les réponses ici en travaillant dans .Net Standard 1.2: /
Dan Rayson

Réponses:


374

Je ne sais pas si j'ai bien compris vos intentions, mais voyons si celle-ci aide.

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get { return (T)Convert.ChangeType(base.Value, typeof(T)); }
        set { base.Value = value.ToString();}
    }
}

Je me demande depuis quelques jours comment désérialiser un flux en un type générique. Merci :)
Piège

3
Je suis d'accord, bien que ce Convert.ChangeTypene soit pas une solution très universelle et extensible, cela fonctionne pour la plupart des types de base. si quelque chose de mieux est nécessaire, il n'y a aucun problème à envelopper cette méthode dans quelque chose de plus grand comme Tim l'a suggéré ou à utiliser une méthode de conversion différente.
lubos hasko

18
J'ajouterais certainement le où T: IConvertible
MikeT

5
Le type T ne doit pas être IConvertible, mais le type de base.La valeur doit.
chapluck

74

La méthode de lubos hasko échoue pour les nullables. La méthode ci-dessous fonctionnera pour nullables. Mais je ne l'ai pas inventé. Je l'ai trouvé via Google: http://web.archive.org/web/20101214042641/http://dogaoztuzun.com/post/C-Generic-Type-Conversion.aspx Crédit à "Tuna Toksoz"

Utilisation d'abord:

TConverter.ChangeType<T>(StringValue);  

La classe est ci-dessous.

public static class TConverter
{
    public static T ChangeType<T>(object value)
    {
        return (T)ChangeType(typeof(T), value);
    }

    public static object ChangeType(Type t, object value)
    {
        TypeConverter tc = TypeDescriptor.GetConverter(t);
        return tc.ConvertFrom(value);
    }

    public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter
    {

        TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
    }
}

j'ajouterais une option de conversion de secours pour les énumérations et autres structures complexes, mais bon appel.
Tomer W

2
Pourquoi le RegisterTypeConverter? Avons-nous besoin d'enregistrer les convertisseurs au préalable? (malheureusement, le lien est mort, donc je n'ai pas pu le lire)
Cohen

Pour plusieurs conversions, vous devez probablement créer tc(la TypeConverter) une seule fois. TypeConverterest lent car il utilise la réflexion pour rechercher le fichier TypeConverterAttribute. Si vous initialisez un seul TypeConverterchamp privé , vous devriez pouvoir réutiliser les TypeConverternombreuses fois.
Kevin P. Rice

Fonctionne bien, mais si T est un object, lève une exception. J'ai pu contourner cela en utilisant `if (typeof (T) .IsPrimitive) {return TConverter.ChangeType <T> (StringValue); } else {object o = (object) StringValue; retourner à; } `en remplacement de l'exemple d'utilisation TConverter.ChangeType<T>(StringValue)
Matt

14

Pour de nombreux types (entier, double, DateTime, etc.), il existe une méthode Parse statique. Vous pouvez l'invoquer en utilisant la réflexion:

MethodInfo m = typeof(T).GetMethod("Parse", new Type[] { typeof(string) } );

if (m != null)
{
    return m.Invoke(null, new object[] { base.Value });
}

8
TypeDescriptor.GetConverter(PropertyObject).ConvertFrom(Value)

TypeDescriptorest une classe ayant une méthode GetConvertorqui accepte un Typeobjet et vous pouvez ensuite appeler la ConvertFromméthode pour convertir le valuepour cet objet spécifié.


Personnellement, je pense que cette interface est meilleure pour gérer la conversion au lieu de la méthode Convert.ChangeType car vous devez implémenter l'interface IConvertible sur toute votre classe.
Sauleil

3

Vous pouvez éventuellement utiliser une construction telle qu'une classe de traits . De cette façon, vous auriez une classe d'assistance paramétrée qui sait convertir une chaîne en une valeur de son propre type. Ensuite, votre getter pourrait ressembler à ceci:

get { return StringConverter<DataType>.FromString(base.Value); }

Maintenant, je dois souligner que mon expérience avec les types paramétrés est limitée au C ++ et à ses modèles, mais j'imagine qu'il existe un moyen de faire la même chose en utilisant des génériques C #.


Les versions génériques n'existent pas en C #.
Shimmy Weitzhandler

3

Vérifiez la statique Nullable.GetUnderlyingType. - Si le type sous-jacent est nul, alors le paramètre de modèle ne l'est pas Nullable, et nous pouvons utiliser ce type directement - Si le type sous-jacent n'est pas nul, alors utilisez le type sous-jacent dans la conversion.

Semble fonctionner pour moi:

public object Get( string _toparse, Type _t )
{
    // Test for Nullable<T> and return the base type instead:
    Type undertype = Nullable.GetUnderlyingType(_t);
    Type basetype = undertype == null ? _t : undertype;
    return Convert.ChangeType(_toparse, basetype);
}

public T Get<T>(string _key)
{
    return (T)Get(_key, typeof(T));
}

public void test()
{
    int x = Get<int>("14");
    int? nx = Get<Nullable<int>>("14");
}

1

En s'inspirant de la réponse de Bob , ces extensions prennent également en charge la conversion de valeur nulle et toutes les conversions primitives de retour et quatrième.

public static class ConversionExtensions
{
        public static object Convert(this object value, Type t)
        {
            Type underlyingType = Nullable.GetUnderlyingType(t);

            if (underlyingType != null && value == null)
            {
                return null;
            }
            Type basetype = underlyingType == null ? t : underlyingType;
            return System.Convert.ChangeType(value, basetype);
        }

        public static T Convert<T>(this object value)
        {
            return (T)value.Convert(typeof(T));
        }
}

Exemples

            string stringValue = null;
            int? intResult = stringValue.Convert<int?>();

            int? intValue = null;
            var strResult = intValue.Convert<string>();

0
public class TypedProperty<T> : Property
{
    public T TypedValue
    {
        get { return (T)(object)base.Value; }
        set { base.Value = value.ToString();}
    }
}

J'utilise la conversion via un objet. C'est un peu plus simple.


merci Je dois convertir un T dans une interface et l'objet de conversion simple T fonctionne correctement, facilement et rapidement merci
LXG

5
(int) (objet) "54"; est une fatalité!, ce n'est pas VB!
Tomer W

0

J'ai utilisé la réponse lobos et cela fonctionne. Mais j'ai eu un problème avec la conversion des doubles à cause des paramètres de culture. J'ai donc ajouté

return (T)Convert.ChangeType(base.Value, typeof(T), CultureInfo.InvariantCulture);

0

Encore une autre variation. Gère les valeurs Nullables, ainsi que les situations où la chaîne est nulle et T ne l' est pas .

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get
        {
            if (base.Value == null) return default(T);
            var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
            return (T)Convert.ChangeType(base.Value, type);
        }
        set { base.Value = value.ToString(); }
    }
}

0

Vous pouvez le faire sur une seule ligne comme ci-dessous:

YourClass obj = (YourClass)Convert.ChangeType(YourValue, typeof(YourClass));

Bon codage;)


Lorsque vous avez partagé du code comme réponse, veuillez essayer de l'expliquer.
Yunus Temurlenk
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.