Analizuję plik XML z XmlReader
klasą w .NET i pomyślałem, że mądrze byłoby napisać ogólną funkcję analizy, aby generalnie odczytywać różne atrybuty. Wymyśliłem następującą funkcję:
private static T ReadData<T>(XmlReader reader, string value)
{
reader.MoveToAttribute(value);
object readData = reader.ReadContentAsObject();
return (T)readData;
}
Jak sobie uświadomiłem, nie działa to do końca tak, jak planowałem; zgłasza błąd w przypadku typów pierwotnych, takich jak int
lub double
, ponieważ rzutowanie nie może zostać przekonwertowane z typu a string
na typ liczbowy. Czy istnieje sposób, aby moja funkcja przeważała w zmodyfikowanej formie?
Czy próbowałeś Convert.ChangeType ?
Jeśli metoda zawsze zwraca ciąg, który wydaje mi się dziwny, ale to nie ma znaczenia, być może ten zmieniony kod zrobiłby to, co chcesz:
private static T ReadData<T>(XmlReader reader, string value) { reader.MoveToAttribute(value); object readData = reader.ReadContentAsObject(); return (T)Convert.ChangeType(readData, typeof(T)); }
źródło
próbować
if (readData is T) return (T)(object)readData;
źródło
Możesz wymagać, aby typ był typem referencyjnym:
private static T ReadData<T>(XmlReader reader, string value) where T : class { reader.MoveToAttribute(value); object readData = reader.ReadContentAsObject(); return (T)readData; }
A następnie wykonaj inną, która używa typów wartości i 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 //... }
źródło
Właściwie problemem tutaj jest użycie ReadContentAsObject. Niestety ta metoda nie spełnia jej oczekiwań; chociaż powinien wykryć najbardziej odpowiedni typ wartości, w rzeczywistości zwraca ciąg, bez względu na wszystko (można to zweryfikować za pomocą Reflectora).
Jednak w twoim konkretnym przypadku znasz już typ, na który chcesz rzutować, dlatego powiedziałbym, że używasz złej metody.
Zamiast tego spróbuj użyć ReadContentAs, jest to dokładnie to, czego potrzebujesz.
private static T ReadData<T>(XmlReader reader, string value) { reader.MoveToAttribute(value); object readData = reader.ReadContentAs(typeof(T), null); return (T)readData; }
źródło
Możesz przypuszczalnie przekazać, jako parametr, delegata, który skonwertuje z ciągu znaków na T.
źródło
Dodaj ograniczenie `` klasy '' (lub bardziej szczegółowe, takie jak klasa bazowa lub interfejs oczekiwanych obiektów T):
private static T ReadData<T>(XmlReader reader, string value) where T : class { reader.MoveToAttribute(value); object readData = reader.ReadContentAsObject(); return (T)readData; }
lub
where T : IMyInterface
lubwhere T : new()
itpźródło
Właściwie odpowiedzi prowadzą do interesującego pytania, co chcesz, aby Twoja funkcja robiła w przypadku błędu.
Może bardziej sensowne byłoby skonstruowanie go w formie metody TryParse, która próbuje wczytać do T, ale zwraca false, jeśli nie można tego zrobić?
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; }
edycja: teraz, kiedy o tym myślę, czy naprawdę muszę wykonać test convert.changetype? czy linia as nie próbuje już tego zrobić? Nie jestem pewien, czy zrobienie tego dodatkowego wywołania typu changetype faktycznie coś da. W rzeczywistości może to po prostu zwiększyć obciążenie przetwarzania, generując wyjątek. Jeśli ktoś wie o różnicy, która sprawia, że warto to zrobić, napisz!
źródło