Generischer TypeConverter

Es wäre doch super, wenn es einen universellen Konverter gäbe, mit dem man einen beliebigen Datentyp in einen beliebigen anderen Datentyp umwandeln könnte? Am besten ohne jegliche Prüfungen auf null und vor allem ohne explizite Behandlung von Exceptions. Ich habe eine gute Neuigkeit, dies ist tatsächlich möglich.

string value = null;
int x = value.ConvertOrDefault<int>();

oder

int x = 0;
value.TryConvert<int>(out x);

Die Anwendung des TypeConverters ist super einfach. Der TypeConverter bietet 2 generische Methoden an: T ConvertOrDefault<T>() und bool TryConvert<T>(out T result)

Der generische Parameter bestimmt den Datentyp, in den umgewandelt werden soll. Es bedarf keinerlei Prüfung auf null und es werden keine Exceptions ausgelöst! Im Falle einer misslungenen Konvertierung, wird ein default Wert zurückgegeben. Dieser default Wert hängt immer vom Zieltyp der Umwandlung ab. z.B. bei einem string, ist der default Wert null, bei einem int ist dieser Wert 0 usw.

Einfache Verwendung durch Extension Methods

Und so funktioniert es. Die generischen Methoden sind zusätzliche ExtensionsMethods (siehe Extension Methods) und sehen folgendermaßen aus:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class Extension
{
        public static T ConvertOrDefault<T>(this object value)
        {
            T result = default(T);
            Converter.TryConvert<T>(value, out result);
 
            return result;
        }
 
        public static bool TryConvert<T>(this object value, out T result)
        {
            return Converter.TryConvert<T>(value, out result);
        }
}

Die Hauptaufgabe dieser Methoden besteht darin, den eigentlichen generischen Converter aufzurufen und im Falle einer fehlgeschlagenen Typumwandlung einen default Wert des jeweiligen Zieltypen zurück zu geben. Ganz nebenbei besitzen Extension Methods eine sehr praktische Eigenschaft. Diese können nämlich aufgerufen werden, auch wenn der Wert der Instanzvariablen null ist, ohne dabei eine Exception zu werfen. Deshalb können wir in unserem Beispiel die Methoden aufrufen, obwohl die Variable myString den Wert null hat.

string value = null;
int x = value.ConvertOrDefault<int>();

Die eigentliche Konvertierung

Die Klasse Converter übernimmt die eigentliche Typumwandlung und stellt dazu eine statische Methode TryConvert<T>(object value, out T result) bereit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Converter
{
    public static bool TryConvert<T>(object value, out T result)
    {
        //Typenspezifischer default Wert wird auf jeden Fall als resultat
        //der Konvertierung verwendet.
        result = default(T);
 
        //result = default(T);
 
        if (value == null || value == DBNull.Value) return false;
 
        //falls der ZielTyp gleich dem Quelltyp ist, führe direkt die Konvertierung durch
        if (typeof(T) == value.GetType())
        {
            result = (T)value;
            return true;
        }
 
        string typeName = typeof(T).Name;
 
        try
        {
            //sollte der Typ Nullable oder ein Enum sein
            //verwende den TypeDescriptor und den TypeConverter
            if (typeName.IndexOf(typeof(System.Nullable).Name,
                                StringComparison.Ordinal) > -1 ||
                                typeof(T).BaseType.Name.IndexOf(typeof(System.Enum).Name,
                                        StringComparison.Ordinal) > -1)
            {
                TypeConverter tc = TypeDescriptor.GetConverter(typeof(T));
                result = (T)tc.ConvertFrom(value);
            }
            else
            {
                result = (T)Convert.ChangeType(value, typeof(T));
            }
        }
        catch
        {
            return false;
        }
 
        return true;
    }
}

Die statische Methode TryConvert nimmt 2 Parameter entgegen, und besitzt einen weiteren generischen Parameter, der den Zieltyp der Umwandlung bestimmt. Object value beinhaltet den Ursprungswert, der umzuwandeln werden soll. out T result ist das Resultat der Umwandlung. Zusätzlich wird noch ein True oder False Wert zurückgegeben, der angibt ob eine Konvertierung erfolgreich war. Der eigentliche Ablauf der Konvertierung ist bereits in den Quelltextkommentaren beschrieben.

Beispiele

Und hier sind einige Anwendungsbeispiele:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
object obj = 1;  
 
string value = null;
int x = 5;
if (value.TryConvert<int>(out x))
    Console.WriteLine("tryconvert beispiel: " + x); // wird nicht aufgerufen
 
Console.WriteLine("tryconvert beispiel: " + x); 
 
bool boolean = "false".ConvertOrDefault<bool>();
bool? nullableBoolean = "".ConvertOrDefault<bool?>();
int integer = obj.ConvertOrDefault<int>();
int negativeInteger = "-12123".ConvertOrDefault<int>();
int? nullableInteger = value.ConvertOrDefault<int?>();
MyEnum enumValue = "SecondValue".ConvertOrDefault<MyEnum>();
 
MyObjectBase myObject = new MyObjectClassA();
MyObjectClassA myObjectClassA = myObject.ConvertOrDefault<MyObjectClassA>();
//wrong convertion
MyObjectClassA myObjectClassAWrong = enumValue.ConvertOrDefault<MyObjectClassA>();
 
Console.WriteLine("booleanValue: " + boolean);                      // false
Console.WriteLine("nullableBoolValue: " + nullableBoolean);         // null
Console.WriteLine("integerValue: " + integer);                      // 1
Console.WriteLine("negativeInteger: " + negativeInteger);           // -12123
Console.WriteLine("nullableInteger: " + nullableInteger);           // null
Console.WriteLine("enumValue: " + enumValue);                       // SecondValue
Console.WriteLine("myObjectClassA: " + myObjectClassA);             // MyObjectClassA
Console.WriteLine("myObjectClassAWrong: " + myObjectClassAWrong);   // null

Ok das war‘s, viel Spaß damit! Und sollte jemand einen Verbesserungsvorschlag haben, oder einen Fehler finden, dann schreib doch einen Kommentar zu diesem Post.


Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

Comment

You may use these tags : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>