Converti stringa in tipo nullable (int, double, ecc …)

Sto tentando di eseguire una conversione dei dati. Sfortunatamente, gran parte dei dati è in stringhe, dove dovrebbe essere int o double, ecc …

Quindi quello che ho è qualcosa di simile:

double? amount = Convert.ToDouble(strAmount); 

Il problema con questo approccio è se strAmount è vuoto, se è vuoto, voglio che diventi nullo, quindi quando lo aggiungo al database la colonna sarà nullo. Così ho finito per scrivere questo:

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

Ora funziona bene, ma ora ho cinque righe di codice invece di una. Questo rende le cose un po ‘più difficili da leggere, specialmente quando ho una grande quantità di colonne da convertire.

Ho pensato di utilizzare un’estensione della class string e di un generico per passare il tipo, questo perché potrebbe essere un double, o un int o un long. Così ho provato questo:

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

Ma ottengo l’errore: Imansible convertire il tipo ‘stringa’ in ‘T?’

C’è un modo per aggirare questo? Non ho molta familiarità con la creazione di metodi che utilizzano i generici.

Un’altra cosa da tenere a mente è che la stringa stessa potrebbe essere nulla.

 public static Nullable ToNullable(this string s) where T: struct { Nullable result = new Nullable(); try { if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0) { TypeConverter conv = TypeDescriptor.GetConverter(typeof(T)); result = (T)conv.ConvertFrom(s); } } catch { } return result; } 

Potresti provare a utilizzare il seguente metodo di estensione:

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

In questo modo puoi farlo:

 double? amount = strAmount.GetValueOrNull(); int? amount = strAmount.GetValueOrNull(); decimal? amount = strAmount.GetValueOrNull(); 

Ho scritto questo convertitore di tipo generico. Funziona con valori Nullable e standard, convertendo tutti i tipi convertibili, non solo le stringhe. Gestisce tutti i tipi di scenari che ti aspetteresti (valori predefiniti, valori nulli, altri valori, ecc …)

L’ho usato per circa un anno in dozzine di programmi di produzione, quindi dovrebbe essere abbastanza solido.

  public static T To(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 (this IConvertible obj) { try { return To(obj); } catch { return default(T); } } public static bool ToOrDefault (this IConvertible obj, out T newObj) { try { newObj = To(obj); return true; } catch { newObj = default(T); return false; } } public static T ToOrOther (this IConvertible obj, T other) { try { return To(obj); } catch { return other; } } public static bool ToOrOther (this IConvertible obj, out T newObj, T other) { try { newObj = To(obj); return true; } catch { newObj = other; return false; } } public static T ToOrNull (this IConvertible obj) where T : class { try { return To(obj); } catch { return null; } } public static bool ToOrNull (this IConvertible obj, out T newObj) where T : class { try { newObj = To(obj); return true; } catch { newObj = null; return false; } } 

Che dire di questo:

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

Ovviamente, questo non tiene conto della mancata conversione.

Potresti provare:

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

fai il tuo controllo null e restituisci int? se necessario. Ti consigliamo anche di completarlo try {}

Dai uno sguardo …

 public delegate bool TryParseDelegate(string data, out T output); public static T? ToNullablePrimitive(this string data, TryParseDelegate func) where T:struct { string.IsNullOrEmpty(data) return null; T output; if (func(data, out output)) { return (T?)output; } return null; } 

Quindi chiamalo così …

 void doStuff() { string foo = "1.0"; double? myDouble = foo.ToNullablePrimitive(double.TryParse); foo = "1"; int? myInt = foo.ToNullablePrimitive(int.TryParse); foo = "haha"; int? myInt2 = foo.ToNullablePrimitive(int.TryParse); } 

Mi piace la risposta di Joel, ma l’ho leggermente modificata perché non sono un fan delle eccezioni.

  ///  /// Converts a string to the specified nullable type. ///  /// The type to convert to /// The string to convert /// The nullable output public static T? ToNullable(this string s) where T : struct { if (string.IsNullOrWhiteSpace(s)) return null; TypeConverter conv = TypeDescriptor.GetConverter(typeof (T)); return (T) conv.ConvertFrom(s); } ///  /// Attempts to convert a string to the specified nullable primative. ///  /// The primitive type to convert to /// The string to convert /// The nullable output ///  /// True if conversion is successfull, false otherwise. Null and whitespace will /// be converted to null and return true. ///  public static bool TryParseNullable(this string data, out T? output) where T : struct { try { output = data.ToNullable(); return true; } catch { output = null; return false; } } 

Puoi usare il seguente con gli oggetti, sfortunatamente questo non funziona con le stringhe.

 double? amount = (double?)someObject; 

Lo uso per il wrapping di una variabile di sessione in una proprietà (su una pagina di base) .. quindi il mio utilizzo effettivo è (nella mia pagina di base):

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

Sono in grado di verificare la logica null nella pagina:

 if (base.OrganisationID == null) // do stuff 

Non c’è modo di aggirarlo. Nullable, così come il tuo metodo, è vincolato ad usare solo tipi di valore come argomento. String è un tipo di riferimento e quindi è incompatibile con questa dichiarazione.

 public static class GenericExtension { public static T? ConvertToNullable(this String s) where T : struct { try { return (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(s); } catch (Exception) { return null; } } } 

Esiste una soluzione generica (per qualsiasi tipo). L’usabilità è buona, ma l’implementazione dovrebbe essere migliorata: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Questo ti permette di scrivere codice molto pulito come questo:

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

e anche:

 object obj = 1; string value = null; int x = 5; if (value.TryConvert(out x)) Console.WriteLine("TryConvert example: " + x); bool boolean = "false".ConvertOrDefault(); bool? nullableBoolean = "".ConvertOrDefault(); int integer = obj.ConvertOrDefault(); int negativeInteger = "-12123".ConvertOrDefault(); int? nullableInteger = value.ConvertOrDefault(); MyEnum enumValue = "SecondValue".ConvertOrDefault(); MyObjectBase myObject = new MyObjectClassA(); MyObjectClassA myObjectClassA = myObject.ConvertOrDefault(); 

Ecco qualcosa in base alla risposta accettata. Ho rimosso il try / catch per assicurarmi che tutte le eccezioni non vengano ingerite e non vengano gestite. Inoltre ho fatto in modo che la variabile di ritorno (nella risposta accettata) non sia mai inizializzata due volte per niente.

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

Il mio esempio per i tipi anonimi:

 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); object nullableInt1 = ConvertNullable("5", anonimousType); // or evident Type Nullable nullableInt2 = (Nullable)ConvertNullable("5", typeof(Nullable)); 

Un’altra variazione. Questo

  • Non ingoiare le eccezioni
  • Genera una NotSupportedException se il tipo non può essere convertito da una string . Ad esempio, una struttura personalizzata senza un convertitore di tipi.
  • Altrimenti restituisce un (T?)null se la stringa non riesce ad analizzare. Non è necessario verificare la presenza di null o spazi vuoti.
 using System.ComponentModel; public static Nullable ToNullable(this string s) where T : struct { var ret = new Nullable(); 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; } 

Aggiungiamo un’altra soluzione simile allo stack. Questo analizza anche le enumerazioni, e sembra carino. Molto sicuro

 ///  /// 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. ///  ///  /// 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. ///  /// Contributed by Taylor Love (Pangamma) ///  ///  ///  ///  public static T? ToNullable(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(p_self, out t)) return t;} } return null; } 

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

La risposta generica fornita da ” Joel Coehoorn ” è buona.

Ma, questo è un altro modo senza usare quei GetConverter... o try/catch blocchi … (non sono sicuro, ma in alcuni casi questo potrebbe avere prestazioni migliori):

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

L’utilizzo è il seguente:

 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