2009-05-22 18 views
59

Ich Parsing eine XML-Datei mit der XmlReader Klasse in. NET und ich dachte, es wäre schlau, eine generische Syntaxanalyse-Funktion zu schreiben, um verschiedene Attribute generisch zu lesen. Ich kam mit der folgenden Funktion:Cast Objekt zu T

private static T ReadData<T>(XmlReader reader, string value) 
{ 
    reader.MoveToAttribute(value); 
    object readData = reader.ReadContentAsObject(); 
    return (T)readData; 
} 

Wie ich zu realisieren begriff, das funktioniert nicht ganz so, wie ich es geplant habe; Es gibt einen Fehler bei primitiven Typen wie int oder double aus, da eine Umwandlung von string in einen numerischen Typ nicht konvertiert werden kann. Gibt es eine Möglichkeit, dass sich meine Funktion in modifizierter Form durchsetzt?

Antwort

144

Zuerst prüfen, ob es gegossen werden kann.

if (readData is T) { 
    return (T)readData; 
} 
try { 
    return (T)Convert.ChangeType(readData, typeof(T)); 
} 
catch (InvalidCastException) { 
    return default(T); 
} 
+1

änderte ich die Zeile mit Convert.ChangeType zu: ‚return (T) Convert.ChangeType (readdata, typeof (T), System.Globalization.CultureInfo.InstalledUICulture.NumberFormat), um es auf verschiedenen kulturellen funktioniert Konfigurationen. –

+1

Ausgezeichnete Antwort. Ich habe Tage in Foren gesucht, aber keine richtige Antwort gefunden. Danke Bob. –

+1

Ich denke, Sie können (T) (Objekt) readData – jjxtra

15

Haben Sie versucht Convert.ChangeType?

Wenn die Methode immer einen String zurückgibt, was ich seltsam finden, aber das ist neben dem Punkt, dann vielleicht geändert dieser Code würde tun, was Sie wollen:

private static T ReadData<T>(XmlReader reader, string value) 
{ 
    reader.MoveToAttribute(value); 
    object readData = reader.ReadContentAsObject(); 
    return (T)Convert.ChangeType(readData, typeof(T)); 
} 
+0

Ich habe zunächst Convert.ChangeType betrachtet, aber entschieden, dass es aus irgendeinem Grund für diesen Vorgang nicht nützlich ist. Du und Bob haben beide die gleiche Antwort gegeben, und ich entschied mich für eine Mischung aus deinen Antworten, sodass ich es vermied, try-Anweisungen zu verwenden, aber immer 'return (T) readData' benutzte, wenn es möglich war. –

1

hinzufügen ‚Klasse‘ Beschränkung (oder mehr detailliert, wie eine Basisklasse oder Schnittstelle Ihres exepected T-Objekte):

private static T ReadData<T>(XmlReader reader, string value) where T : class 
{ 
    reader.MoveToAttribute(value); 
    object readData = reader.ReadContentAsObject(); 
    return (T)readData; 
} 

oder where T : IMyInterface oder where T : new(), etc

2

Sie können vermutlich pas s-in, als Parameter, einen Delegierten, die von Zeichenfolge T. konvertieren

3

Sie die Art erfordern könnte ein Referenztyp sein:

private static T ReadData<T>(XmlReader reader, string value) where T : class 
{ 
    reader.MoveToAttribute(value); 
    object readData = reader.ReadContentAsObject(); 
    return (T)readData; 
} 

Und dann noch tun, die Wert verwendet Typen und TryParse ...

private static T ReadDataV<T>(XmlReader reader, string value) where T : struct 
{ 
    reader.MoveToAttribute(value); 
    object readData = reader.ReadContentAsObject(); 
    int outInt; 
    if(int.TryParse(readData, out outInt)) 
     return outInt 
    //... 
} 
+0

Die zweite Methode funktioniert nicht, tut es. – mtman

6

versuchen

if (readData is T) 
    return (T)(object)readData; 
1

Eigentlich sind die Antworten eine interessante Frage bringen, das ist Was möchten Sie im Fehlerfall tun?

Vielleicht wäre es sinnvoller, es in Form einer TryParse-Methode zu konstruieren, die versucht, in T zu lesen, aber false zurückgibt, wenn es nicht möglich ist?

private static bool ReadData<T>(XmlReader reader, string value, out T data) 
    { 
     bool result = false; 
     try 
     { 
      reader.MoveToAttribute(value); 
      object readData = reader.ReadContentAsObject(); 
      data = readData as T; 
      if (data == null) 
      { 
       // see if we can convert to the requested type 
       data = (T)Convert.ChangeType(readData, typeof(T)); 
      } 
      result = (data != null); 
     } 
     catch (InvalidCastException) { } 
     catch (Exception ex) 
     { 
      // add in any other exception handling here, invalid xml or whatnot 
     } 
     // make sure data is set to a default value 
     data = (result) ? data : default(T); 
     return result; 
    } 

edit: jetzt, wo ich darüber nachdenke, muss ich wirklich den convert.changetype Test tun? Versucht die as-Linie das nicht schon? Ich bin mir nicht sicher, ob das Ausführen dieses zusätzlichen ChangeType-Calls tatsächlich etwas bewirkt. Tatsächlich könnte es den Verarbeitungsaufwand durch das Erzeugen einer Ausnahme erhöhen. Wenn jemand einen Unterschied kennt, der sich lohnt, bitte posten!

2

Eigentlich ist das Problem hier die Verwendung von ReadContentAsObject. Leider wird diese Methode ihren Erwartungen nicht gerecht; Während es den am besten geeigneten Typ für den Wert erkennen sollte, gibt es tatsächlich eine Zeichenfolge zurück, egal was passiert (dies kann mit Reflector überprüft werden).

In Ihrem speziellen Fall kennen Sie jedoch bereits den Typ, auf den Sie umwandeln möchten, daher würde ich sagen, dass Sie die falsche Methode verwenden.

Verwenden Sie stattdessen ReadContentAs, genau das, was Sie brauchen.

private static T ReadData<T>(XmlReader reader, string value) 
{ 
    reader.MoveToAttribute(value); 
    object readData = reader.ReadContentAs(typeof(T), null); 
    return (T)readData; 
} 
+0

Sieht ziemlich kompakt und elegant aus. Die Lösung in der akzeptierten Antwort verwendet jedoch ChangeType, die mit mehreren verschiedenen Kulturen kompatibel ist, da sie einen IFormatProvider akzeptiert. Da dies eine Notwendigkeit für das Projekt ist, werde ich mich an diese Lösung halten. –