2012-04-25 10 views
5

sto creando un convertitore genericoImpossibile modificare il tipo di nullable nel metodo generico

Ecco un codice di esempio del convertitore generica

bool TryReaderParse<TType>(object data, out TType value) 
{ 
    value = default(TType); 
    Type returnType = typeof(TType); 
    object tmpValue = null; 

    if (returnType == typeof(DateTime)) 
    { 
     tmpValue = StringToDatetime(data.ToString()); 
    } 
    else if (returnType == typeof(DateTime?)) // THIS IF FIRES 
    { 
     tmpValue = StringToNullableDatetime(data.ToString()); 
    } 

    value = (TType)Convert.ChangeType(tmpValue, returnType); // THROWS 
} 

public DateTime? StringToNullableDatetime(string date) 
{ 
    DateTime? datetime = null; 
    if (!string.IsNullOrEmpty(date)) 
    { 
     datetime = DateTime.Parse(date, new CultureInfo(Resources.CurrentCulture)); 
    } 

    return datetime; 
} 

E questo è come lo uso:

void foo() 
{ 
    DateTime? date = null; 
    TryReaderParse<DateTime?>("25/12/2012", out date); 
} 

L'eccezione generata dice che non è possibile convertire da DateTime a Nullable<DateTime>. Dal momento che il metodo crea e restituisce un tipo nullable, come mai il casting fallisce?

Alla fine, voglio avere un DateTime nullable, in questo particolare esempio.

modificare Il problema è che StringToNullableDatetime metodo restituisce un Datetime? e il casting dice che non si può convertire da Datetime

Poiché il metodo StringToNullableDatetime restituisce un datetime nullable, come è possibile che il Convert.ChangeType non può vedere che il passato l'argomento è nullable?

Ps. Ho letto risposte come this una che fa il contrario (casting da nullable).

risposta

18

L'eccezione generata indica che non è possibile convertire da DateTime a Nullable<DateTime>. Dal momento che il metodo crea e restituisce un tipo nullable, come mai il casting fallisce?

Buona domanda. Ciò non riesce perché non esiste nulla come un valore nullo scatolato. Quando converti un valore da DateTime? a object, ottieni un riferimento null, se il numero DateTime? era nullo o se ottieni il valore in scatola, uno DateTime. Non si ottiene mai una struttura nullable inscatolata; non vi è nulla di simile.

Pertanto, si finisce con null o con un DateTime valido in quella casella. Dici quindi Converti per convertirlo in un valore DateTime nullable e Converti non sa come farlo.

Il mio consiglio è di abbandonare completamente questa linea di attacco; questo codice è alquanto offensivo dei generici. Ogni volta che fai un passaggio sul tipo specifico di un generico, il tuo codice non è più generico e probabilmente stai sbagliando. Se si vuole fare una "prova" metodo di stile per i datetimes poi basta scrivere che:

DateTime? TryReadDateTime(object data) 
{ 
    ... return null if the object cannot be read as a datetime ... 
} 

Scrivi un tale metodo per ogni tipo che si intende leggere. L'utente sarebbe molto meglio scrivere:

DateTime? d = TryReadDateTime(data); 
if (d != null) ... 

Than

DateTime d; 
bool b = TryRead<DateTime>(data, out d); 
if (b) ... 
0

Dalla documentation, questa linea errore se:

valore è nullo e conversiontype è un tipo di valore

Nullable<T> è una struct e quindi un tipo di valore, pertanto non è possibile utilizzare questa chiamata di metodo se il valore è nullo. Gestisci già le date separatamente, quindi perché utilizzare lo ChangeType in questi casi?

+0

modificato la mia domanda. Il mio problema è che non posso restituire un datetime Nullable. La riga 'Convert.ChangeType' non può vedere che l'argomento passato sia annullabile – Odys

0

Il modo in cui nullable, generici e boxe interagiscono è strano. Si può essere meglio definire due metodi:

 
bool TryReaderParse(object data, out TType value); 
bool TryReaderParse(object data, out TType? value) where TType : struct; 

All'interno del secondo metodo, il codice può semplicemente produrre un TType e assegnarlo alla TType? senza difficoltà.

+2

Prima di tutto, un metodo non può essere sovraccaricato solo sui vincoli. In secondo luogo, se il metodo restituisce un valore nullable allora * perché deve anche restituire un bool *? Se hai intenzione di fare ciò, le firme giuste sono 'T TryParseClass (dati oggetto) dove T: class' e' T? TryParseStruct (dati oggetto) dove T: struct'. Nessun bool necessario. –