Convertissez la chaîne de caractères en type nullable (int, double, etc.…)

j'essaie de faire une conversion de données. Malheureusement, la plupart des données sont dans des chaînes, où il devrait être int's ou double, etc...

alors ce que j'ai c'est quelque chose comme:

double? amount = Convert.ToDouble(strAmount);

le problème avec cette approche est que si strAmount est vide, s'il est vide, je veux qu'il soit nul, donc quand je l'ajoute dans la base de données, la colonne sera nulle. Alors j'ai fini par écrire ceci:

double? amount = null;
if(strAmount.Trim().Length>0)
{
    amount = Convert.ToDouble(strAmount);
}

maintenant ceci fonctionne très bien, mais j'ai maintenant cinq lignes de code au lieu d'un. Cela rend les choses un peu plus difficile à lire, surtout quand j'ai une grande quantité de colonnes à convertir.

je pensais utiliser une extension de la classe string et générique de passer dans le type, c'est parce qu'il pourrait être un double, ou un int ou un long. J'ai donc essayé ceci:

    public static class GenericExtension
    {
        public static Nullable<T> ConvertToNullable<T>(this string s, T type) where T: struct
        {
            if (s.Trim().Length > 0)
            {
                return (Nullable<T>)s;
            }
            return null;
        }
    }

mais j'obtiens l'erreur: ne peut pas convertir le type 'string' en ' T?

est il y a un moyen de contourner cela? Je ne suis pas très familier avec la création de méthodes utilisant des génériques.

125
demandé sur John Saunders 2009-04-21 19:10:03

16 réponses

une autre chose à garder à l'esprit est que la chaîne elle-même pourrait être nulle.

public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
    Nullable<T> result = new Nullable<T>();
    try
    {
        if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0)
        {
            TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
            result = (T)conv.ConvertFrom(s);
        }
    }
    catch { } 
    return result;
}
144
répondu Joel Coehoorn 2009-06-02 13:16:18

vous pouvez essayer d'utiliser la méthode d'extension ci-dessous:

public static T? GetValueOrNull<T>(this string valueAsString)
    where T : struct 
{
    if (string.IsNullOrEmpty(valueAsString))
        return null;
    return (T) Convert.ChangeType(valueAsString, typeof(T));
}

de cette façon vous pouvez faire ceci:

double? amount = strAmount.GetValueOrNull<double>();
int? amount = strAmount.GetValueOrNull<int>();
decimal? amount = strAmount.GetValueOrNull<decimal>();
47
répondu Michael Meadows 2009-04-21 15:24:34

j'ai écrit ce convertisseur de type générique. Il fonctionne avec des valeurs nullables et standard, la conversion entre tous les types convertibles - pas seulement chaîne de caractères. Il gère toutes sortes de scénarios auxquels vous vous attendez (valeurs par défaut, valeurs nulles, autres valeurs, etc...)

Je l'utilise depuis environ un an dans des douzaines de programmes de production, donc ça devrait être assez solide.

    public static T To<T>(this IConvertible obj)
    {
        Type t = typeof(T);

        if (t.IsGenericType
            && (t.GetGenericTypeDefinition() == typeof(Nullable<>)))
        {
            if (obj == null)
            {
                return (T)(object)null;
            }
            else
            {
                return (T)Convert.ChangeType(obj, Nullable.GetUnderlyingType(t));
            }
        }
        else
        {
            return (T)Convert.ChangeType(obj, t);
        }
    }

    public static T ToOrDefault<T>
                 (this IConvertible obj)
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return default(T);
        }
    }

    public static bool ToOrDefault<T>
                        (this IConvertible obj,
                         out T newObj)
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = default(T);
            return false;
        }
    }

    public static T ToOrOther<T>
                           (this IConvertible obj,
                           T other)
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return other;
        }
    }

    public static bool ToOrOther<T>
                             (this IConvertible obj,
                             out T newObj,
                             T other)
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = other;
            return false;
        }
    }

    public static T ToOrNull<T>
                          (this IConvertible obj)
                          where T : class
    {
        try
        {
            return To<T>(obj);
        }
        catch
        {
            return null;
        }
    }

    public static bool ToOrNull<T>
                      (this IConvertible obj,
                      out T newObj)
                      where T : class
    {
        try
        {
            newObj = To<T>(obj);
            return true;
        }
        catch
        {
            newObj = null;
            return false;
        }
    }
22
répondu TheSoftwareJedi 2009-06-02 13:24:18

:


double? amount = string.IsNullOrEmpty(strAmount) ? (double?)null : Convert.ToDouble(strAmount);

bien sûr, cela ne tient pas compte de l'échec du converti.

21
répondu John Kraft 2009-04-21 15:21:30

Vous pouvez essayer:

TypeConverter conv = TypeDescriptor.GetConverter(typeof(int));
conv.ConvertFrom(mystring);

faites votre propre contrôle null et retournez int? si nécessaire. Vous voudrez aussi envelopper cela dans un try {}

8
répondu Andrew Bullock 2009-04-21 15:12:58

faites un essai...

public delegate bool TryParseDelegate<T>(string data, out T output);

public static T? ToNullablePrimitive<T>(this string data, 
    TryParseDelegate<T> func) where T:struct
{
    string.IsNullOrEmpty(data) return null;

    T output;

    if (func(data, out output))
    {
        return (T?)output;
    }

    return null;
}

alors appelez ça comme ça...

void doStuff()
{
    string foo = "1.0";

    double? myDouble = foo.ToNullablePrimitive<double>(double.TryParse);

    foo = "1";

    int? myInt = foo.ToNullablePrimitive<int>(int.TryParse);

    foo = "haha";

    int? myInt2 = foo.ToNullablePrimitive<int>(int.TryParse);
}
6
répondu Adam Robinson 2009-04-21 15:25:54

j'aime la réponse de Joel, mais je l'ai légèrement modifiée car je ne suis pas un fan de manger des exceptions.

    /// <summary>
    /// Converts a string to the specified nullable type.
    /// </summary>
    /// <typeparam name="T">The type to convert to</typeparam>
    /// <param name="s">The string to convert</param>
    /// <returns>The nullable output</returns>
    public static T? ToNullable<T>(this string s) where T : struct
    {
        if (string.IsNullOrWhiteSpace(s))
            return null;

        TypeConverter conv = TypeDescriptor.GetConverter(typeof (T));
        return (T) conv.ConvertFrom(s);
    }

    /// <summary>
    /// Attempts to convert a string to the specified nullable primative.
    /// </summary>
    /// <typeparam name="T">The primitive type to convert to</typeparam>
    /// <param name="data">The string to convert</param>
    /// <param name="output">The nullable output</param>
    /// <returns>
    /// True if conversion is successfull, false otherwise.  Null and whitespace will
    /// be converted to null and return true.
    /// </returns>
    public static bool TryParseNullable<T>(this string data, out T? output) where T : struct
    {
        try
        {
            output = data.ToNullable<T>();
            return true;
        }
        catch
        {
            output = null;
            return false;
        }
    }
5
répondu Colin Place 2011-02-02 18:23:36

vous pouvez utiliser ce qui suit avec des objets, malheureusement cela ne fonctionne pas avec des chaînes.

double? amount = (double?)someObject;

Je l'utilise pour envelopper une variable de session dans une propriété (sur une page de base).. ainsi, mon utilisation actuelle est (dans ma page de base):

public int? OrganisationID
{
    get { return (int?)Session[Constants.Session_Key_OrganisationID]; }
    set { Session[Constants.Session_Key_OrganisationID] = value; }
}

je suis capable de vérifier pour null dans la logique de la page:

if (base.OrganisationID == null)
    // do stuff
5
répondu Scotty.NET 2012-07-06 10:44:57

il n'y a pas d'autre solution. Nullable, ainsi que votre méthode est contraint d'utiliser uniquement les types de valeur comme argument. String est un type de référence et est donc incompatible avec cette déclaration.

3
répondu JaredPar 2009-04-21 15:11:24
public static class GenericExtension
{
    public static T? ConvertToNullable<T>(this String s) where T : struct 
    {
        try
        {
            return (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(s);
        }
        catch (Exception)
        {
            return null;
        }
    }
}
3
répondu Daniel Brückner 2009-04-21 15:28:03

il existe une solution générique (pour tout type). La convivialité est bonne, mais la mise en œuvre doit être améliorée: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter /

cela vous permet d'écrire du code très propre comme ceci:

string value = null;
int? x = value.ConvertOrDefault<int?>();

et aussi:

object obj = 1;  

string value = null;
int x = 5;
if (value.TryConvert(out x))
    Console.WriteLine("TryConvert example: " + x); 

bool boolean = "false".ConvertOrDefault<bool>();
bool? nullableBoolean = "".ConvertOrDefault<bool?>();
int integer = obj.ConvertOrDefault<int>();
int negativeInteger = "-12123".ConvertOrDefault<int>();
int? nullableInteger = value.ConvertOrDefault<int?>();
MyEnum enumValue = "SecondValue".ConvertOrDefault<MyEnum>();

MyObjectBase myObject = new MyObjectClassA();
MyObjectClassA myObjectClassA = myObject.ConvertOrDefault<MyObjectClassA>();
3
répondu Pavel Hodek 2013-01-30 07:39:30

voici quelque chose basé sur une réponse acceptée. J'ai enlevé le try / catch pour m'assurer que toutes les exceptions ne sont pas avalées et ne sont pas traitées. Assurez-vous également que la variable de retour (dans la réponse acceptée) n'est jamais initialisée deux fois pour rien.

public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
    if (!string.IsNullOrWhiteSpace(s))
    {
        TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));

        return (T)conv.ConvertFrom(s);
    }

    return default(Nullable<T>);
}
3
répondu PhilDulac 2016-05-04 15:35:20

Mon exemple pour l'anonymat types:

private object ConvertNullable(object value, Type nullableType)
{
    Type resultType = typeof(Nullable<>).MakeGenericType(nullableType.GetGenericArguments());
    return Activator.CreateInstance(resultType, Convert.ChangeType(value, nullableType.GetGenericArguments()[0]));
}

...

Type anonimousType = typeof(Nullable<int>);
object nullableInt1 = ConvertNullable("5", anonimousType);
// or evident Type
Nullable<int> nullableInt2 = (Nullable<int>)ConvertNullable("5", typeof(Nullable<int>));
2
répondu ADM-IT 2014-12-09 13:05:06

autre variante. Celui-ci

  • n'avale pas les exceptions
  • lance un NotSupportedException si le type ne peut pas être converti de string . Par exemple, une structure personnalisée sans convertisseur de type.
  • renvoie par ailleurs un (T?)null si la chaîne ne parvient pas à se Parser. Pas besoin de vérifier pour null ou whitespace.
using System.ComponentModel;

public static Nullable<T> ToNullable<T>(this string s) where T : struct
{
    var ret = new Nullable<T>();
    var conv = TypeDescriptor.GetConverter(typeof(T));

    if (!conv.CanConvertFrom(typeof(string)))
    {
        throw new NotSupportedException();
    }

    if (conv.IsValid(s))
    {
        ret = (T)conv.ConvertFrom(s);
    }

    return ret;
}
2
répondu BurnsBA 2017-11-08 20:54:56

ajoutons une solution similaire à la pile. Celui-ci aussi parse enums, et il semble agréable. Très sûr.

/// <summary>
    /// <para>More convenient than using T.TryParse(string, out T). 
    /// Works with primitive types, structs, and enums.
    /// Tries to parse the string to an instance of the type specified.
    /// If the input cannot be parsed, null will be returned.
    /// </para>
    /// <para>
    /// If the value of the caller is null, null will be returned.
    /// So if you have "string s = null;" and then you try "s.ToNullable...",
    /// null will be returned. No null exception will be thrown. 
    /// </para>
    /// <author>Contributed by Taylor Love (Pangamma)</author>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="p_self"></param>
    /// <returns></returns>
    public static T? ToNullable<T>(this string p_self) where T : struct
    {
        if (!string.IsNullOrEmpty(p_self))
        {
            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
            if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self);
            if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;}
        }

        return null;
    }

https://github.com/Pangamma/PangammaUtilities-CSharp/blob/master/PangammaUtilities/Extensions/ToNullableStringExtension.cs

0
répondu Pangamma 2017-02-24 18:22:33

la réponse générique fournie par " Joel Coehoorn " est bonne.

mais, c'est une autre façon sans utiliser ces blocs GetConverter... ou try/catch ... (je ne suis pas sûr, mais cela peut avoir une meilleure performance dans certains cas):

public static class StrToNumberExtensions
{
    public static short ToShort(this string s, short defaultValue = 0) => short.TryParse(s, out var v) ? v : defaultValue;
    public static int ToInt(this string s, int defaultValue = 0) => int.TryParse(s, out var v) ? v : defaultValue;
    public static long ToLong(this string s, long defaultValue = 0) => long.TryParse(s, out var v) ? v : defaultValue;
    public static decimal ToDecimal(this string s, decimal defaultValue = 0) => decimal.TryParse(s, out var v) ? v : defaultValue;
    public static float ToFloat(this string s, float defaultValue = 0) => float.TryParse(s, out var v) ? v : defaultValue;
    public static double ToDouble(this string s, double defaultValue = 0) => double.TryParse(s, out var v) ? v : defaultValue;

    public static short? ToshortNullable(this string s, short? defaultValue = null) => short.TryParse(s, out var v) ? v : defaultValue;
    public static int? ToIntNullable(this string s, int? defaultValue = null) => int.TryParse(s, out var v) ? v : defaultValue;
    public static long? ToLongNullable(this string s, long? defaultValue = null) => long.TryParse(s, out var v) ? v : defaultValue;
    public static decimal? ToDecimalNullable(this string s, decimal? defaultValue = null) => decimal.TryParse(s, out var v) ? v : defaultValue;
    public static float? ToFloatNullable(this string s, float? defaultValue = null) => float.TryParse(s, out var v) ? v : defaultValue;
    public static double? ToDoubleNullable(this string s, double? defaultValue = null) => double.TryParse(s, out var v) ? v : defaultValue;
}

d'Utilisation est le suivant:

var x1 = "123".ToInt(); //123
var x2 = "abc".ToInt(); //0
var x3 = "abc".ToIntNullable(); // (int?)null 
int x4 = ((string)null).ToInt(-1); // -1
int x5 = "abc".ToInt(-1); // -1

var y = "19.50".ToDecimal(); //19.50

var z1 = "invalid number string".ToDoubleNullable(); // (double?)null
var z2 = "invalid number string".ToDoubleNullable(0); // (double?)0
0
répondu S.Serpooshan 2018-08-01 11:50:24