2012-04-25 13 views
5

ich einen generischen KonverterKann nicht Typen in generischer Methode zum NULLABLE ändern

Hier ist ein Beispielcode des generischen Konverter zu schaffen

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

Und das ist, wie ich es verwenden:

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

Die geworfene Ausnahme besagt, dass sie nicht von DateTime in Nullable<DateTime> konvertieren kann. Da die Methode einen NULL-fähigen Typ erstellt und zurückgibt, wie schlägt das Casting fehl?

Am Ende möchte ich eine Nullable DateTime haben, in diesem speziellen Beispiel.

bearbeiten Das Problem ist, dass StringToNullableDatetime Methode ein Datetime? zurückkehrt und das Casting sagt, dass nicht von Datetime

konvertiert Da die StringToNullableDatetime Methode eine Nullable-Datetime zurückgibt, wie es möglich ist, dass die Convert.ChangeType nicht, dass die bestandenen sehen Argument ist nullfähig?

Ps. Ich habe Antworten wie this gelesen, die das Gegenteil tun (Casting von Nullable).

Antwort

18

Die geworfene Ausnahme besagt, dass sie nicht von DateTime in Nullable<DateTime> konvertieren kann. Da die Methode einen NULL-fähigen Typ erstellt und zurückgibt, wie schlägt das Casting fehl?

Gute Frage. Dies schlägt fehl, weil gibt es keine solche boxed Nullable. Wenn Sie eine DateTime?-object konvertieren, entweder Sie einen NULL-Verweis erhalten, wenn die DateTime? null war, oder Sie den boxed Wert, eine DateTime bekommen. Du erhältst niemals eine geboxte Nullable-Struktur. Es gibt keine solche Sache.

Daher enden Sie mit Null oder eine gültige DateTime in diesem Feld. Anschließend weisen Sie Convert an, das in ein nullable DateTime zu konvertieren, und Convert weiß nicht, wie das geht.

Mein Rat ist, dass Sie diese Angriffslinie vollständig aufgeben; Dieser Code ist grenzwertig missbräuchlich von Generika. Jedes Mal, wenn Sie den spezifischen Typ eines Generic einschalten, ist Ihr Code nicht mehr Generic und Sie tun es wahrscheinlich falsch. Wenn Sie ein „versuchen“ -Stil Methode für Datetimes tun wollen, dann schreiben Sie einfach, dass:

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

schreiben, ein solches Verfahren für jeder Art, die Sie lesen möchten. Der Benutzer würde viel lieber schreiben:

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

als

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

Vom documentation, wird diese Linie Fehler, wenn:

Wert ist null und conversionType ist ein Werttyp

Nullable<T> eine Struktur ist und somit ein Werttyp, so kann man nicht Verwenden Sie diesen Methodenaufruf, wenn Ihr Wert null ist. Sie behandeln Daten bereits separat, warum also in diesen Fällen ohnehin ChangeType?

+0

meine Frage bearbeitet. Mein Problem ist, dass ich keine nullbare Datetime zurückgeben kann. Die 'Convert.ChangeType'-Zeile kann nicht sehen, dass das übergebene Argument nullable ist – Odys

0

Die Art, wie NULL-Zeichen, Generika und Boxen interagieren, ist seltsam. Sie können besser dran, die zwei Methoden sein:

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

In der zweiten Methode können Sie den Code einfach eine TType produzieren und es in die TType? problemlos zuordnen.

+2

Zunächst einmal kann eine Methode nicht nur für Constraints überladen werden. Zweitens, wenn die Methode einen nullbaren Wert zurückgibt, dann * warum muss sie auch einen bool * zurückgeben? Wenn Sie das tun werden, dann sind die richtigen Signaturen 'T TryParseClass (Objektdaten) wo T: Klasse' und' T? TryParseStruct (Objektdaten) wobei T: struct'. Kein Bool notwendig. –