Converter string em tipo anulável (int, double, etc…)

Eu estou tentando fazer alguma conversão de dados. Infelizmente, grande parte dos dados está em strings, onde deve ser int ou double, etc …

Então o que eu tenho é algo como:

double? amount = Convert.ToDouble(strAmount); 

O problema com esta abordagem é se o strAmount estiver vazio, se estiver vazio, eu quero que ele seja nulo, então quando eu adicioná-lo ao database, a coluna será nula. Então acabei escrevendo isso:

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

Agora isso funciona bem, mas agora tenho cinco linhas de código em vez de uma. Isso torna as coisas um pouco mais difíceis de ler, especialmente quando eu tenho uma grande quantidade de colunas para converter.

Eu pensei que eu usaria uma extensão para a class de strings e genéricos para passar no tipo, isto é porque poderia ser um double, ou um int, ou um longo. Então eu tentei isso:

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

Mas recebo o erro: Não é possível converter o tipo ‘string’ para ‘T’?

Existe uma maneira de contornar isso? Não estou muito familiarizado com a criação de methods usando genéricos.

Outra coisa a ter em mente é que a string em si pode 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; } 

Você poderia tentar usar o método de extensão abaixo:

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

Desta forma você pode fazer isso:

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

Eu escrevi este conversor de tipo genérico. Ele funciona com valores padrão e Nullable, convertendo entre todos os tipos conversíveis – não apenas string. Ele lida com todos os tipos de cenários que você esperaria (valores padrão, valores nulos, outros valores, etc …)

Eu tenho usado isso por cerca de um ano em dezenas de programas de produção, então deve ser bem 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; } } 

O que sobre isso:

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

Claro, isso não leva em conta a falha do convertido.

Você pode querer tentar:

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

faça sua própria verificação nula e retorne int? se necessário. Você também vai querer envolver isso em uma try {}

Dê um tiro …

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

Então ligue assim …

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

Eu gosto da resposta de Joel, mas modifiquei ligeiramente, já que não sou fã de comer exceções.

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

Você pode usar o seguinte com objects, infelizmente isso não funciona com seqüências de caracteres embora.

 double? amount = (double?)someObject; 

Eu uso para embrulhar uma variável de session em uma propriedade (em uma página de base) .. então meu uso real é (na minha página de base):

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

Eu posso verificar por null na lógica da página:

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

Não há maneira de contornar isso. Anulável, assim como seu método, é restrito a usar apenas tipos de valor como argumento. String é um tipo de referência e, portanto, é incompatível com essa declaração.

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

Existe uma solução genérica (para qualquer tipo). A usabilidade é boa, mas a implementação deve ser melhorada: http://cleansharp.de/wordpress/2011/05/generischer-typeconverter/

Isso permite que você escreva um código muito limpo como este:

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

e também:

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

Aqui está algo com base na resposta aceita. Eu removi o try / catch para garantir que todas as exceções não fossem engolidas e não fossem tratadas. Também garantiu que a variável de retorno (em resposta aceita) nunca é inicializada duas vezes 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); } 

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

Outra variação Este

  • Não engole exceções
  • Lança um NotSupportedException se o tipo não puder ser convertido da string . Por exemplo, uma estrutura personalizada sem um conversor de tipos.
  • Caso contrário, retorna um (T?)null se a string não for analisada. Não há necessidade de verificar nulo ou espaço em branco.
 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; } 

Vamos adicionar mais uma solução semelhante à pilha. Este também analisa enums, e parece bom. Muito 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

A resposta genérica fornecida por ” Joel Coehoorn ” é boa.

Mas, esta é outra maneira sem usar os GetConverter... ou try/catch blocos … (não tenho certeza, mas isso pode ter um melhor desempenho em alguns 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; } 

O uso é como seguindo:

 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