2009-04-27 8 views
45

Attualmente uso questo metodo di estensione conversione pratico per fare conversioni tra tipi:Come posso risolvere il problema con la conversione generica a Nullable <T>?

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

Tuttavia, non gradisce conversione dei valori validi Nullable, per esempio, ciò non riesce:

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

Ovviamente , 1 è facilmente convertito in una, ma ottiene l'errore (int?):

Invalid cast from 'System.String' to 'System.Nullable`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'. 

Questo è un esempio, ovviamente semplificata, in realtà ci sono ing che faccia conversioni tipi di stringhe in questo modo:

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

Se Convert.ChangeType non piace Nullable, chiunque hanno tutte le grandi idee?

+0

Vedere la mia domanda: http://stackoverflow.com/questions/773078/c-convert-string-to-nullable-type-int-double-etc –

risposta

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

Grazie Luca! Questo è perfetto. Era t.GetGenericTypeDefinition() == typeof (Nullable <>)) e Nullable.GetUnderlyingType (t) che mi ha davvero aiutato qui. Questa è roba buona. Grazie ancora. – TheSoftwareJedi

+1

A seconda delle esigenze è utile modificare if (obj == null) TO if (obj == null || obj is String && obj.ToString() == "") –

+0

Come una leggera semplificazione/chiarimento, se T è nullable, e obj è nullo, non 'return default (T)' sempre uguale a semplicemente 'return null'? – goodeye

2

Forse mi manca il punto, ma nell'istanza di Nullable, in che modo il tuo metodo fornisce un vantaggio di leggibilità, prestazioni o manutenzione su un cast semplice, come (int?)1?

A parte questo, forse un altro metodo di estensione?

public static T? ToNullable<T>(this T obj) where T:struct 
{ 
    return (T?)obj; 
} 

Modifica

Dopo aver esaminato la tua modifica, perché avrebbe la funzione generica che ho fornito non funzionare come un sostituto per la funzione To<T> in quella riga di codice? Non è possibile consentire una conversione a Nullable per qualsiasi tipo (motivo per cui ChangeType non funziona) poiché tale generico accetta solo tipi di valore. Dovrai utilizzare una funzione come quella che ho fornito o modificare la tua firma di To<T> per accettare solo tipi di valore e aggiungere un caso speciale per Nullable<T>.

+0

C'è un modo per trasferirlo nel mio metodo esistente e rilevare il nullable generico e gestirlo in modo appropriato? – TheSoftwareJedi

+0

Beh, si potrebbe dire questo ... if (typeof (T) .IsGeneric && typeof (T) .GetGenericTypeDefinition(). Name.StartsWith ("Nullable")) ti dirà se il tipo è annullabile. È quindi possibile applicare il caso speciale, ma non sono sicuro che possa funzionare quando A accetta tipi non strutturati. Quale sarebbe il problema di cambiare semplicemente per usare ToNullable quando è quello che vuoi? –

1

questo è il metodo che attualmente utilizzo (ho avuto la mia risposta su SO), converte da stringa a tipo nullable:

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

Ma se la conversione funzionato, viene generata un'eccezione , non restituire un null come dovrebbe essere previsto.

+0

buona risposta, perfettamente in linea, ma posso solo assegnare correttamente a 1, e Luke fa il controllo nullo. Saluti! – TheSoftwareJedi

2

soluzione di Luca era buono per me (e, ovviamente, ha ottenuto il suo voto su), ma ho semplificato per me in questo modo

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

perché ho iniziato da una stringa non da un tipo ... pensieri?

3

ho finito con questo

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

Si comporta come i metodi di estensione LINQ Single e SingleOrDefault e First e FirstOrDefault.

In breve, To<T>() tenta di convertire e genera in caso di errore mentre ToOrDefault<T>() tenta di convertire e restituisce default(T) in caso di errore.

0

estendono @LukeH codice:

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

Aggiungi qualche spiegazione alla tua risposta. –

+0

se il valore letterale è nullo, restituire DefaultValue. altrimenti converti letterale in T Type. –

0

Questo metodo fa quello che ti serve, e sembra bello allo stesso tempo.

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

Problemi correlati