2009-04-27 10 views
45

Actualmente uso de este método de extensión de conversión práctica de hacer conversiones entre tipos:¿Cómo puedo solucionar esto para hacer la conversión genérica a Nullable <T>?

public static T To<T>(this IConvertible obj) 
    { 
     return (T)Convert.ChangeType(obj, typeof(T)); 
    } 

Sin embargo, no le gusta la conversión de los valores válidos para anulable, por ejemplo, esta falla:

"1".To<int?>(); 

Obviamente , 1 se convierte fácilmente en una, pero se pone el error (int?):

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 simplificado, obviamente, en la realidad que nos soy ing que haga las conversiones de tipos de cadenas de este modo:

packageDb.Quantity = package.package.ElementDeep(Namespace + "PackageQuantity", Namespace + "ActualQuantity", Namespace + "Quantity").ValueOrNull().To<int?>(); 

Si Convert.ChangeType no le gusta anulable, cualquier persona tiene grandes ideas?

+0

Ver mi pregunta: http://stackoverflow.com/questions/773078/c-convert-string-to-nullable-type-int-double-etc –

Respuesta

94
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); 
    } 
} 
+0

Gracias Luke! Esto es perfecto. Fue t.GetGenericTypeDefinition() == typeof (Nullable <>)) y Nullable.GetUnderlyingType (t) que realmente me ayudó aquí. Esto es bueno. Gracias de nuevo. – TheSoftwareJedi

+1

Dependiendo de sus necesidades es útil cambiar if (obj == null) TO if (obj == null || obj is String && obj.ToString() == "") –

+0

Como una ligera simplificación/aclaración, si T es nulo, y obj es nulo, ¿no es 'return default (T)' siempre lo mismo que simplemente 'return null'? – goodeye

2

Tal vez me esté faltando el punto, pero en el caso de Nullable, ¿cómo proporciona su método una ventaja de legibilidad, rendimiento o mantenimiento sobre un simple molde, como (int?)1?

Aparte de eso, ¿quizás 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 tu edición, ¿por qué la función genérica que proporcioné no funcionar como un sustituto a 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 valores. Deberá utilizar una función como la que proporcioné o cambiar su firma de To<T> para aceptar solo tipos de valores y agregar un caso especial para Nullable<T>.

+0

¿Hay una manera de rodar esto en mi método existente, y hacer que detecte el genérico que admite nulos, y manejarlo apropiadamente? – TheSoftwareJedi

+0

Bueno, podría decir esto ... if (typeof (T) .IsGeneric && typeof (T) .GetGenericTypeDefinition(). Name.StartsWith ("Nullable") le dirá si el tipo es anulable. A continuación, puede aplicar el caso especial, pero no estoy seguro de que pueda funcionar cuando A acepta tipos no estructurales. ¿Cuál sería el problema de simplemente cambiar para usar ToNullable cuando eso es lo que quieres? –

1

Este es el método que yo uso actualmente (Tengo mi respuesta en SO), que 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; 
    } 
4
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, se lanzará una excepción , no devuelve un valor nulo como debería esperarse.

+0

buena respuesta, perfectamente en la pista, pero solo puedo adjudicar la correcta a 1, y la de Lucas hace la comprobación nula. ¡Aclamaciones! – TheSoftwareJedi

2

solución de Lucas era bueno para mí (y, obviamente, tuvo su Answer) pero simplificado para mí 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 empecé a partir de una cadena no de un tipo ... pensamientos?

3

he terminado con esta

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 de LINQ Single y SingleOrDefault y First y FirstOrDefault.

En resumen, To<T>() intenta convertir y arroja el error, mientras que ToOrDefault<T>() intenta convertir y devuelve default(T) en caso de error.

0

extienden @LukeH código:

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

Agregue un poco de explicación a su respuesta. –

+0

si su Literal es nulo, devuelva DefaultValue. de lo contrario, convierte Literal en T Type. –

0

Este método hace lo que necesita, y se ve bien mientras lo hace.

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

Cuestiones relacionadas