Peter Bucher - Mein Experiment, meine Spielereien, meine Welt...   ·   Stefan Falz   ·   Jürgen Gutsch   ·   Golo Roden   ·   ASP.NET Zone   ·   Microsoft ASP.NET
Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

Typkonvertierung vereinfacht, oder: <Value>.ToOrDefault<Type>();

*Update

Im Zuge meiner Arbeit an der SessionFacade ging mir das Licht auf, dass wir es im Optimalfall mit relativ wenigen Session-Variablen zu tun haben und über eine SessionFacade auch einen einfachen typisierten Zugriff gewähren können.
Zudem finden wir in der Session im Normalfall nur geboxte Typen, die mit einem einfachen Cast zu ihrem Urprungstyp gewandelt werden können.
Da die .To-Methoden nur für das Parsen vom Typ String in einen anderen Typen gedacht ist, bringt es nichts bspw. ein geboxter int zu einem String zu wandeln um diesen nochmals zu parsen, wenn ein Cast reicht.

Zudem ist die Angabe von "object" als Zieltyp für eine Extensionmethode nicht sehr gut gewählt, da sie dann bei allen Typen erscheint. Vorallem dann natürlich nicht, wenn es relativ unsinnig ist.

Im Zuge dieser Erkenntnis habe ich den Zieltyp auf "string" geändert.
Zusätzlich gibt es auch noch die Anforderung bei einem nicht erfolgreifen Parsing auf einen anderen Typen, einen Startwert zu setzen, der vom Standardwert des Typen abweicht.

Ich habe das Beispiel unten geändert und die Erläuterung plus ein Beispiel für die zweite Überladung mit dem Standardwert hinzugefügt. Viel Spass!

Insbesondere bei der Webentwicklung mit .NET haben wir viel mit Eingaben als Strings zu tun.
Diese müssen dann in int, DateTime, double, Guid, etc... konvertiert werden.

.NET bietet dabei <Type>.Parse()- / <Type>.TryParse()- oder ConvertTo<Type>-Methoden an.

Hierbei gilt die Empfehlung <Type>.TryParse() zu verwenden um auf eine nicht mögliche Konvertierung aufgrund von Nullwerten oder ungültigen Werten zu reagieren.

Die ganze Geschichte gibt aber viel sich immer wiederholende Schreibarbeit, ein Extrembeispiel der Konvertierung eines Strings (Oder null-Literal!) aus einer GET-Parameter Abfrage zu einem int könnte mit <Type>.TryParse() so aussehen:



string value = Request.QueryString["index"]; // Kann auch das Literal "null" darstellen 
int typedValue;

if(int.TryParse(value, out typedValue) && typedValue == 4) {
    lblStatus.Text = string.Format("Wert gültig: {0}", typedValue);
}

Für eine Konvertierung, die so häufig vorkommt ist das mir - auch aufgrund von anderen Überlegungen - viel zu viel Schreibarbeit.
Ich stelle mir sowas vor:



int typedValue = Request.QueryString["index"].ToOrDefault<int>();

if(typedValue == 4) {
    lblStatus.Text = string.Format("Wert gültig: {0}", typedValue);
}

Zuerst meine Überlegungen:
Ob eine Konvertierung erfolgreich war, brauche ich nur den Wert zu überprüfen, einen Boolean ob die Konvertierung erfolgreich war, brauche ich nicht.
Bei int reicht mir eine Prüfung auf den Stardardwert 0, bei Guid die Prüfung auf Guid.Empty, bei DateTime entweder DateTime.MinValue.
Sind es boolsche Werte oder kann man / möchte man .MinValue von DateTime nicht benutzen, nimmt man sich Nullable Types zur Hilfe.

Mehr braucht es meines Erachtens nicht um primitive Typen sowie erweiterte Strukturen zu konvertieren und festzustellen was angekommen ist.

Ich habe mir dazu eine Methode geschrieben, die nach Wunsch entweder als Extension Method oder als statische Hilfsmethode in einer Tools-Klasse genutzt werden kann. (Das Original ist hier zu finden)

Die Implementierung:



public static partial class Common
{
    /// <summary>
    /// Gibt ein typisiertes Objekt aus einem String zurück.
    /// Falls die Umwandlung nicht erfolgreich ist, wird stattdessen
    /// der Standardwert des Zieltypen zurückgegeben.
    /// </summary>
    /// <typeparam name="T">Zieltyp</typeparam>
    /// <param name="value">Zu typisierenden String</param>
    /// <param name="defaultValue">Standardwert (Wird bei Fehlschlag zurückgegeben)</param>
    /// <returns>Typisiertes Objekt</returns>
    public static T ToOrDefault<T>(this string value, T defaultValue)
    {
      T result = defaultValue;

      if (value == null)
        return result;

      TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
      if (converter.CanConvertFrom(typeof(string)))
      {
        try
        {
          result = (T)converter.ConvertFrom(value);
        } catch (Exception) {}
      }

      return result;
    }




    /// <summary>
    /// Gibt ein typisiertes Objekt aus einem String zurück.
    /// Falls die Umwandlung nicht erfolgreich ist, wird stattdessen
    /// der Standardwert des Zieltypen zurückgegeben.
    /// </summary>
    /// <typeparam name="T">Zieltyp</typeparam>
    /// <param name="value">Zu typisierenden String</param>
    /// <returns>Typisiertes Objekt</returns>
    public static T ToOrDefault<T>(this string value) {
        return value.ToOrDefault<T>(default(T));
    }
}

Die Nutzung sieht dann bspw. so aus:



// Nullable
DateTime? date = Request.QueryString["date"].ToOrDefault<DateTime?>();
if(date.HasValue) {
    <meinObjekt>.Date = date.Value;
}

// Kein Nullable
Guid guid = Request.Form["userGuid"].ToOrDefault<Guid>();
if(guid != Guid.Empty)
    currentUser = User.Load(guid);
// Standardwert
int width = Request.QueryString["width"].ToOrDefault<int>(1024);
// width == 1024 bei erfolglosem Parsing (Wenn bspw. der Parameter null zurückgibt)
// width == 4 bei erfolgreiem Parsing bei einem Eingangsstring von "4"
// Oder ohne Angabe des Typparameters (Typ wird implizit durch die Angabe des Standardwertes ermittelt)
int height = Request.QueryString["height"].ToOrDefault(768);

Diese Methode ist dafür gedacht um Strings oder geboxte Strings (Bspw. ein object das von Session[<Index>] zurückkommt aber ein String enhält in die typisierte Version zu konvertieren.
Double zu int oder eigene Objekte funktionieren nicht, dafür gibt es Casts oder man schreibt sich eigene Typekonverter dafür.
Bei normalen Typen (int, string, double, ...) wird bei der Eingabe von null oder ungültigen Strings jeweils der .NET-Standardwert (int = 0, Guid = Guid.Empty, ...) geliefert, bei der Verwendung von Nullable Types kann dann eine Prüfung gegen null benutzt werden.

Was denkt ihr?
Benutzt die Kommentarfunktion!

Bearbeitung / Korrekturen:
17.12.08 - Möglichkeit zur Angabe eines Standardwertes hinzugefügt (Inspiriert durch Golo Roden)
19.12.08 - Die .To-Methode vereinfacht (Danke an Golo Roden)
19.12.08 - Die Methode von "To()" nach "ToOrDefault()" umbenannt.

Veröffentlicht Montag, 8. Dezember 2008 21:29 von Peter Bucher

Kommentare

# re: Typkonvertierung vereinfacht, oder: <Value>.To<Type>();

klingt sehr gut. muss ich mal in einer ruhigen minute in ein projekt von mir einbauen um das wirklich live durchzutesten.

auf so ein snippet warte ich schon lange - die klassischen überprüfungen finde ich einfach nur nervig und overkill.

Montag, 8. Dezember 2008 22:15 by jolli

# re: Typkonvertierung vereinfacht, oder: <Value>.To<Type>();

Hi Peter,

große Klasse :-)!

Viele Grüße,

Golo

Mittwoch, 10. Dezember 2008 08:45 by Golo Roden

# re: Typkonvertierung vereinfacht, oder: <Value>.To<Type>();

Hi Peter,

das ganze ist richtig toll geworden :-)! Und auch danke, dass Du das ganze mit dem Standardwert noch gemacht hast - super :-)!

Ich frag mich nur dauernd, woher ich dieses Codeschnippsel im Beispiel kenne ;-).

Viele Grüße,

Golo

Mittwoch, 17. Dezember 2008 11:09 by Golo Roden

# re: Typkonvertierung vereinfacht, oder: <Value>.To<Type>();

Hallo Golo

> Ich frag mich nur dauernd, woher ich dieses Codeschnippsel im Beispiel kenne ;-)

Mir ist nichts kreatives eingefallen, aber ich habs auswendig getippt ;-)

Mittwoch, 17. Dezember 2008 19:07 by Peter Bucher
Anonyme Kommentare sind nicht zugelassen