String in nullbaren Typ konvertieren (int, double, etc…)

137

Ich versuche eine Datenkonvertierung durchzuführen. Leider befinden sich viele Daten in Zeichenfolgen, in denen es sich um int oder double usw. handeln sollte.

Was ich also habe, ist so etwas wie:

double? amount = Convert.ToDouble(strAmount);

Das Problem bei diesem Ansatz ist, wenn strAmount leer ist. Wenn es leer ist, soll der Wert null sein. Wenn ich ihn also zur Datenbank hinzufüge, ist die Spalte null. Also schrieb ich Folgendes:

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

Jetzt funktioniert das gut, aber ich habe jetzt fünf Codezeilen anstelle von einer. Dies macht das Lesen etwas schwieriger, insbesondere wenn ich eine große Anzahl von Spalten konvertieren muss.

Ich dachte, ich würde eine Erweiterung der String-Klasse und der Generika verwenden, um den Typ zu übergeben. Dies liegt daran, dass es sich um ein Double, ein Int oder ein Long handeln kann. Also habe ich das versucht:

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;
    }
}

Aber ich bekomme die Fehlermeldung: Kann Typ 'string' nicht in 'T konvertieren?'

Gibt es einen Weg, dies zu umgehen? Ich bin nicht sehr vertraut mit dem Erstellen von Methoden mit Generika.

Nathan Koop
quelle
1
Mögliches Duplikat von Generic TryParse
Michael Freidgeim

Antworten:

157

Beachten Sie auch, dass die Zeichenfolge selbst möglicherweise null ist.

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;
}
Joel Coehoorn
quelle
2
Sie können den Parameter "T-Typ" weglassen, da er nicht verwendet wird.
Michael Meadows
1
+1, Schlag mich einfach. Ein kleiner Trottel: Der konvertierte Wert muss direkt dem Ergebnis zugewiesen werden, nicht dem Ergebnis. dh "Ergebnis = (T) conv.ConvertFrom (s);".
LukeH
20
Dies kann ein wenig mit string.IsNullOrWhiteSpace () vereinfacht werden, wenn Sie .Net4
Sergej Andrejev
1
@andrefadila - Zu verwenden: string sampleVendorId = ""; int? vendorId = sampleVendorId.ToNullable <int> ();
Minerva
1
Der Aufruf conv.ConvertFrom konvertiert keinen nullbaren Typ von T, was diese Funktion ein wenig kontraintuitiv macht. In dieser Funktion benötigen Sie keinen Try-Catch. Diese drei Codezeilen machen alles: if (string.IsNullOrWhiteSpace (stringObject)) return null; var conv = TypeDescriptor.GetConverter (typeof (T)); return (T?) conv.ConvertFrom (stringObject);
David
54

Sie können versuchen, die folgende Erweiterungsmethode zu verwenden:

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

So können Sie Folgendes tun:

double? amount = strAmount.GetValueOrNull<double>();
int? amount = strAmount.GetValueOrNull<int>();
decimal? amount = strAmount.GetValueOrNull<decimal>();
Michael Meadows
quelle
3
IMHO ist dies die eleganteste Lösung für das Problem
Zaffiro
4
Eigentlich ... diese Lösung funktioniert nicht. Changetype wird nicht in nullfähige Typen konvertiert. Verwenden Sie stattdessen Typeconverter
AaronHS
Das muss ich wissen ... Ich muss den zugrunde liegenden Typ eines Nullable-Typs verwenden, wenn ich die Convert.ChangeType-Methode verwende. Weil es mit einem Nullable-Typ für den Parameter convertType nicht funktioniert.
Marcus.D
27

Was ist damit:


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

Dies berücksichtigt natürlich nicht den Konvertierungsfehler.

John Kraft
quelle
Wenn Sie einen der Rückgabewerte in ein Double umwandeln? (oder int?, etc), dann kann es sie in das endgültige Double konvertieren. Siehe die Änderung oben.
Bdukes
Das tut mir leid. Vergessen Sie immer die Besetzung, bis der Compiler schreit. :)
John Kraft
Dies schlägt fehl, wenn Sie nicht null sind und den Betrag versuchen. Hat den Wert und deklariert den Betrag als var.
Steve
23

Ich habe diesen generischen Typkonverter geschrieben. Es funktioniert mit Nullable- und Standardwerten und konvertiert zwischen allen konvertierbaren Typen - nicht nur zwischen Zeichenfolgen. Es behandelt alle Arten von Szenarien, die Sie erwarten würden (Standardwerte, Nullwerte, andere Werte usw.)

Ich benutze dies seit ungefähr einem Jahr in Dutzenden von Produktionsprogrammen, daher sollte es ziemlich solide sein.

    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;
        }
    }
TheSoftwareJedi
quelle
2
Ich denke nicht, dass es richtig ist, alle Konvertierungsfehler zu ignorieren. Auch sollten Sie wahrscheinlich nicht alle Arten von Ausnahmen schlucken. Zumindest erneut werfen, OutOfMemoryExceptionwenn Sie es nicht auf einen festen Satz von Ausnahmetypen eingrenzen können.
Paul Groke
9

Vielleicht möchten Sie versuchen:

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

Führen Sie Ihre eigene Nullprüfung durch und geben Sie sie int?gegebenenfalls zurück. Sie werden das auch in a einwickeln wollentry {}

Andrew Bullock
quelle
6

Probieren Sie es aus ...

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;
}

Dann nenne es so ...

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);
}
Adam Robinson
quelle
6

Ich mag Joels Antwort, aber ich habe sie leicht modifiziert, da ich kein Fan von Essensausnahmen bin.

    /// <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;
        }
    }
Colin Place
quelle
5

Sie können Folgendes mit Objekten verwenden, dies funktioniert jedoch leider nicht mit Zeichenfolgen.

double? amount = (double?)someObject;

Ich verwende es zum Umschließen einer Sitzungsvariablen in eine Eigenschaft (auf einer Basisseite). Meine tatsächliche Verwendung ist also (auf meiner Basisseite):

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

Ich kann in der Seitenlogik nach Null suchen:

if (base.OrganisationID == null)
    // do stuff
Scotty.NET
quelle
Hallo danke das hat es für mich gelöst. Zu Ihrer Information, ich habe VB.NET verwendet, und die VB-Äquivalenz CType(Object, Nullable(Of Double))funktioniert gut mit Strings
Rayzinnz
Gibt es eine Version Ihres ersten Beispiels, die mit Zeichenfolgen verwendet werden kann?
Wazz
3

Daran führt kein Weg vorbei. Nullable sowie Ihre Methode dürfen nur Werttypen als Argument verwenden. String ist ein Referenztyp und daher mit dieser Deklaration nicht kompatibel.

JaredPar
quelle
3
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;
        }
    }
}
Daniel Brückner
quelle
3

Es gibt eine generische Lösung (für jeden Typ). Die Benutzerfreundlichkeit ist gut, aber die Implementierung sollte verbessert werden: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Auf diese Weise können Sie sehr sauberen Code wie folgt schreiben:

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

und auch:

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>();
Pavel Hodek
quelle
Wer hat abgestimmt, bitte fügen Sie einen Kommentar hinzu, was mit dieser universellen Lösung nicht stimmt.
Pavel Hodek
1
Nun, zuerst stimmt etwas nicht mit Ihrer Antwort, und das ist "Sie können alle anderen Antworten vergessen". Was falsch wäre, selbst wenn es wahr wäre (was es nicht ist). Und was an der "universellen Lösung" falsch ist, ist, dass sie voller schlechter Leistung ( typeName.IndexOf? Wirklich?) Und seltsamem Verhalten ist (die gezeigte TryConvertFunktion behandelt Nullwerte nicht einmal richtig).
Paul Groke
3

Hier ist etwas basierend auf der akzeptierten Antwort. Ich habe den Versuch / Fang entfernt, um sicherzustellen, dass nicht alle Ausnahmen verschluckt und nicht behandelt werden. Stellen Sie außerdem sicher, dass die Rückgabevariable (in der akzeptierten Antwort) niemals zweimal umsonst initialisiert wird.

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>);
}
PhilDulac
quelle
2

Mein Beispiel für anonyme Typen:

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>));
EINGESTEHEN
quelle
2

Eine andere Variante. Dieses

  • Schluckt keine Ausnahmen
  • Wirft ein, NotSupportedExceptionwenn der Typ nicht konvertiert werden kann string. Zum Beispiel eine benutzerdefinierte Struktur ohne Typkonverter.
  • Andernfalls wird a zurückgegeben, (T?)nullwenn die Zeichenfolge nicht analysiert werden kann. Sie müssen nicht nach Nullen oder Leerzeichen suchen.
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;
}
BurnsBA
quelle
1

Fügen wir dem Stapel eine weitere ähnliche Lösung hinzu. Dieser analysiert auch Enums und sieht gut aus. Sehr sicher.

/// <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

Pangamma
quelle
0

Die generische Antwort von " Joel Coehoorn " ist gut.

Dies ist jedoch ein anderer Weg, ohne diese GetConverter...oder try/catchBlöcke zu verwenden ... (Ich bin nicht sicher, aber dies kann in einigen Fällen eine bessere Leistung haben):

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;
}

Die Verwendung ist wie folgt:

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
S. Serpooshan
quelle
@MassimilianoKraus mag sein, aber es ist ein einfacher 12-Zeilen-Code, der einmal geschrieben wurde, aber immer verwendet wird. Und wie gesagt, es sollte / kann schneller sein als die Verwendung dieser TypeDescriptor.GetConverter... Codes. Dies ist nur ein anderer Weg.
S.Serpooshan