Convierte cadena a tipo anulable (int, double, etc …)

Estoy intentando hacer una conversión de datos. Desafortunadamente, gran parte de la información está en cadenas, donde debería ser int o doble, etc.

Entonces, lo que tengo es algo así como:

double? amount = Convert.ToDouble(strAmount); 

El problema con este enfoque es si strAmount está vacío, si está vacío, quiero que importe para que sea nulo, así que cuando lo agregue a la base de datos, la columna será nula. Así que terminé escribiendo esto:

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

Ahora esto funciona bien, pero ahora tengo cinco líneas de código en lugar de una. Esto hace que las cosas sean un poco más difíciles de leer, especialmente cuando tengo una gran cantidad de columnas para convertir.

Pensé que usaría una extensión para la clase de cadenas y generics para pasar el tipo, esto es porque podría ser un doble, un int o un largo. Así que probé esto:

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

Pero me aparece el error: ¿No se puede convertir el tipo ‘cadena’ en ‘T’?

¿Hay alguna forma de evitar esto? No estoy muy familiarizado con la creación de métodos con generics.

Otra cosa a tener en cuenta es que la cadena en sí podría ser nula.

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

Puede intentar usar el siguiente método de extensión:

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

De esta manera puedes hacer esto:

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

Escribí este convertidor de tipo genérico. Funciona con valores Nullable y estándar, convirtiendo entre todos los tipos convertibles, no solo en cadenas. Maneja todo tipo de escenarios que usted esperaría (valores predeterminados, valores nulos, otros valores, etc.)

He estado usando esto durante aproximadamente un año en docenas de progtwigs de producción, por lo que debería ser bastante sólido.

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

¿Qué tal esto?

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

Por supuesto, esto no tiene en cuenta el error de conversión.

Es posible que desee intentar:

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

hacer su propia comprobación nula y devolver int? si necesario. También querrás envolverlo en una try {}

Dale una oportunidad …

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

Entonces llámalo así …

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

Me gusta la respuesta de Joel, pero la modifiqué ligeramente, ya que no soy partidario de comer excepciones.

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

Puede usar lo siguiente con objetos, desafortunadamente esto no funciona con cadenas.

 double? amount = (double?)someObject; 

Lo uso para envolver una variable de sesión en una propiedad (en una página base) … así que mi uso real es (en mi página base):

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

Puedo verificar null en la lógica de la página:

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

No hay forma de evitar esto. Nullable, al igual que su método, está limitado a usar solo tipos de valores como argumento. String es un tipo de referencia y, por lo tanto, es incompatible con esta statement.

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

Hay una solución genérica (para cualquier tipo). La usabilidad es buena, pero la implementación debería mejorarse: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Esto le permite escribir un código muy limpio como este:

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

y también:

 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(); 

Aquí hay algo basado en la respuesta aceptada. Eliminé el try / catch para asegurarme de que no se traten todas las excepciones y no se solucionen. También se aseguró de que la variable de retorno (en respuesta aceptada) nunca se inicialice dos veces por nada.

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

Mi ejemplo para tipos anónimos:

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

Otra variación Éste

  • No traga excepciones
  • Lanza una NotSupportedException si el tipo no se puede convertir desde una string . Por ejemplo, una estructura personalizada sin un convertidor de tipo.
  • De lo contrario, devuelve un (T?)null si la cadena no puede analizar. No es necesario verificar nulos o espacios en blanco.
 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; } 

Agreguemos una solución más similar a la stack. Este también analiza enums, y se ve bien. Muy seguro.

 ///  /// 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 respuesta genérica proporcionada por ” Joel Coehoorn ” es buena.

Pero esta es otra forma sin usar esos GetConverter... o try/catch bloques … (no estoy seguro, pero esto puede tener un mejor rendimiento en algunos casos):

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

El uso es el siguiente:

 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