Rzut obiektu na T
Analizuję plik XML z klasą XmlReader
W. NET i pomyślałem, że byłoby mądrze napisać generyczną funkcję parsowania, aby ogólnie odczytać 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 zdałem sobie sprawę, nie działa to całkowicie tak, jak zaplanowałem; rzuca błąd w prymitywnych typach, takich jak int
lub double
, ponieważ cast nie może przekształcić się z string
na typ numeryczny. Czy jest jakiś sposób, aby moja funkcja przeważyła w zmodyfikowanej formie?
8 answers
Najpierw sprawdź, czy można go rzucić.
if (readData is T) {
return (T)readData;
}
try {
return (T)Convert.ChangeType(readData, typeof(T));
}
catch (InvalidCastException) {
return default(T);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-19 15:18:01
Czy próbowałeś przekonwertować.ChangeType ?
Jeśli metoda zawsze zwraca ciąg znaków, co wydaje mi się dziwne, ale to nie ma znaczenia, to 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));
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-22 19:42:15
Try
if (readData is T)
return (T)(object)readData;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-22 19:43:16
Możesz wymagać, aby Typ był typem odniesienia:
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
//...
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-22 19:42:49
Możesz prawdopodobnie przekazać, jako parametr, delegata, który konwertuje z ciągu znaków na T.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-22 19:42:37
Właściwie problemem jest użycie ReadContentAsObject. Niestety, ta metoda nie spełnia swoich oczekiwań; chociaż powinna wykryć najbardziej odpowiedni typ dla wartości, w rzeczywistości zwraca łańcuch znaków, bez względu na wszystko (można to sprawdzić za pomocą reflektora).
Jednak w twoim konkretnym przypadku, znasz już typ, do którego chcesz rzucić, dlatego powiedziałbym, że używasz złej metody.
Spróbuj użyć ReadContentAs zamiast tego, to jest dokładnie to, co Ty potrzeba.
private static T ReadData<T>(XmlReader reader, string value)
{
reader.MoveToAttribute(value);
object readData = reader.ReadContentAs(typeof(T), null);
return (T)readData;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-29 21:02:30
Dodaj ograniczenie 'class' (lub bardziej szczegółowe, jak klasa bazowa lub interfejs wybranych 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
lub where T : new()
, itd
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-22 19:42:32
Właściwie, odpowiedzi przywołują interesujące pytanie, czyli to, 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 odczytać w 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;
}
Edit: teraz, kiedy o tym myślę, czy naprawdę muszę zrobić konwersję.test typu zmiany? czy linia as już tego nie robi? Nie jestem pewien, czy robienie tego dodatkowego wywołania typu changetype właściwie to wszystko. W rzeczywistości może to po prostu zwiększyć narzut przetwarzania, generując wyjątek. Jeśli ktoś zna różnicę, która sprawia, że warto to zrobić, proszę pisać!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-22 21:56:30