c# - valor - tipo de dato null
¿Cómo puedo solucionar esto para hacer una conversión genérica a Nullable<T>? (8)
Este es el método que utilizo actualmente (obtuve mi respuesta en SO ), se convierte de cadena a tipo anulable:
public static Nullable<T> ConvertToNullable<T>(this string s) where T : struct
{
if (!string.IsNullOrEmpty(s.Trim()))
{
TypeConverter conv = TypeDescriptor.GetConverter(typeof(Nullable<>).MakeGenericType(typeof(T)));
return (Nullable<T>)conv.ConvertFrom(s);
}
return null;
}
Actualmente utilizo este práctico método de extensión de conversión para hacer conversiones entre tipos:
public static T To<T>(this IConvertible obj)
{
return (T)Convert.ChangeType(obj, typeof(T));
}
Sin embargo, no le gusta convertir valores válidos a Nullable, por ejemplo, esto falla:
"1".To<int?>();
Obviamente, 1 se convierte fácilmente en un (int?), Pero recibe el error:
Invalid cast from ''System.String'' to ''System.Nullable`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]''.
Este es un ejemplo obviamente simplificado, en realidad lo estoy usando para hacer conversiones de tipos de cadenas como las siguientes:
packageDb.Quantity = package.package.ElementDeep(Namespace + "PackageQuantity", Namespace + "ActualQuantity", Namespace + "Quantity").ValueOrNull().To<int?>();
Si a Convert.ChangeType no le gusta Nullable, ¿alguien tiene grandes ideas?
Este método hace lo que necesitas, y se ve bien mientras lo haces.
/// <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/tree/master/src/StringExtensions
He terminado con esto
private static T To<T>(this Object @object, Boolean returnDefaultOnException)
{
Type type = typeof(T);
Type underlyingTypeOfNullable = Nullable.GetUnderlyingType(type);
try
{
return (T) Convert.ChangeType(@object, underlyingTypeOfNullable ?? type);
}
catch (Exception exception)
{
if (returnDefaultOnException)
return default(T);
String typeName = type.Name;
if (underlyingTypeOfNullable != null)
typeName += " of " + underlyingTypeOfNullable.Name;
throw new InvalidCastException("Object can''t be cast to " + typeName, exception);
}
}
public static T To<T>(this Object @object) { return @object.To<T>(returnDefaultOnException: false); }
public static T ToOrDefault<T>(this Object @object) { return @object.To<T>(returnDefaultOnException: true); }
Se comporta como los métodos de extensión LINQ Single
y SingleOrDefault
y First
y FirstOrDefault
.
En resumen, To<T>()
intenta convertir y arroja en caso de error, mientras que ToOrDefault<T>()
intenta convertir y devuelve el default(T)
en caso de error.
La solución de Luke fue buena para mí (y obviamente obtuvo su voto), pero la simplifiqué de esta manera.
private static Type ResolveType(String typeName)
{
Type t = Type.GetType(typeName);
if (t == null)
return null;
Type u = Nullable.GetUnderlyingType(t);
if (u != null) {
t = u;
}
return t;
}
porque comencé desde una cadena no desde un tipo ... ¿pensamientos?
Tal vez me esté perdiendo el punto, pero en el caso de Nullable, ¿cómo proporciona su método una ventaja de legibilidad, rendimiento o mantenimiento sobre un modelo simple, como (int?)1
?
Aparte de eso, tal vez otro método de extensión?
public static T? ToNullable<T>(this T obj) where T:struct
{
return (T?)obj;
}
Editar
Después de revisar su edición, ¿por qué la función genérica que proporcioné no funcionaría como un sustituto de su función To<T>
en esa línea de código? No puede permitir una conversión a Nullable para ningún tipo (por lo que ChangeType
no funciona) porque ese genérico solo acepta tipos de valor. Tendrá que usar una función como la que proporcioné o cambiar su firma de To<T>
para aceptar solo tipos de valor y agregar un caso especial para Nullable<T>
.
extender el código @LukeH:
public static T GetValue<T>(string Literal, T DefaultValue)
{
if (Literal == null || Literal == "" || Literal == string.Empty) return DefaultValue;
IConvertible obj = Literal;
Type t = typeof(T);
Type u = Nullable.GetUnderlyingType(t);
if (u != null)
{
return (obj == null) ? DefaultValue : (T)Convert.ChangeType(obj, u);
}
else
{
return (T)Convert.ChangeType(obj, t);
}
}
public static T To<T>(this IConvertible obj)
{
Type t = typeof(T);
Type u = Nullable.GetUnderlyingType(t);
if (u != null)
{
return (obj == null) ? default(T) : (T)Convert.ChangeType(obj, u);
}
else
{
return (T)Convert.ChangeType(obj, t);
}
}
public static T To<T>(this IConvertible obj)
{
Type t = typeof(T);
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
t = t.GetGenericArguments()[0];
return (T)Convert.ChangeType(obj, t);
}
Pero si la conversión falla, lanzará una excepción, no devolverá un valor nulo como debería esperarse.