Laden...

System.Reflection(setValue) TargetException trotz Typumwandlung

Erstellt von Stoffel000 vor einem Jahr Letzter Beitrag vor einem Jahr 370 Views
S
Stoffel000 Themenstarter:in
11 Beiträge seit 2021
vor einem Jahr
System.Reflection(setValue) TargetException trotz Typumwandlung

Hallo an alle im Forum. Ich habe leider ein Problem mit dem schreiben von Werten in eine Klasse.
Egal um welchen Typ es sich handelt (string, int, double, double?), ich bekomme immer folgende Fehlermeldung

Fehlermeldung:
System.Reflection.TargetException: "Das Objekt stimmt mit dem Zieltyp nicht überein."

Dies passiert in folgendem Codeausschnitt:


foreach (PropertyInfo p in pi) // Propertys aus meiner Klasse
{
    if (dicTemp.TryGetValue(p.Name.ToLower(), out var val)) // Dictionary von einer Datei aus einer anderen Anwendung
    {
        try
        {
            Type etype = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;
            p.SetValue(p.Name, Convert.ChangeType(val, etype)); // <-- Hier erhalte ich die Exception
        }
        catch { } // Nur zum testen damit Programm weiter läuft
    }
continue; // hier zu Testzwecken ein Abbruch

Beispiele zum verdeutlichen:
Format: string: "01.01.2022 00:00:00" aus dem Dictionary soll in ein DateTime-Format geschrieben werden
Format: string: "11" aus dem Dictionary soll in ein Integer-Format geschrieben werden

In beiden Fällen ist die Umwandlung ohne Fehler abgeschlossen und der Wert wird mit dem entsprechenden Datentyp angezeigt.

Zum Test habe ich auch versucht einen Konstanten wert


int test = 12;
p.SetValue(p.Name, test);

oder auch


string test = "12";
p.SetValue(p.Name, (int)test);

versucht. Leider bekomme ich immer die gleiche Fehlermeldung. Die Properties der Klasse verfügen alle über get{} und set{} und können somit beschrieben werden.

2.079 Beiträge seit 2012
vor einem Jahr

C# ist kein JavaScript, man kann einen String nicht in ein int casten.

Die Convert.ChangeType-Methode versucht stattdessen zu parsen, aber zuverlässig ist das auch nicht.
Ich würde in so einem Fall lieber einzeln auf jeden Datentyp prüfen und je die richtige Parse-Methode nutzen.
Convert.ChangeType ist dann nur noch die letzte Alternative.

Mit .NET 7 gibt's auch ein ein IParsable-Interface, was das deutlich einfacher machen sollte.

Abgesehen davon:
Was für ein Dateiformat hast Du? CSV?
Wenn es ein bekanntes Format ist, dann greife lieber auf bestehende Frameworks zurück.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
Stoffel000 Themenstarter:in
11 Beiträge seit 2021
vor einem Jahr

Hallo Palladin007,

Das Dateiformat ist wild. Es ist kein bekanntes Format, das irgendwelchen Regeln folgt.

Das mit der direkten Konvertierung habe ich auch versucht.


if(p.PropertyType == typeof(DateTime)
{
    if(DateTime.TryParse(value, out DateTime dt))
    {
        p.SetValue(p.Name, dt); // <-- Auch hier erhalte ich den Fehler
    }
}

Selbst bei dieser direkten Konvertierung klappt es leider nicht. Der Fehler ist der gleiche.
Die Convert.ChangeType-Methode hatte ich nur versucht, weil es mit der direkten Methode nicht funktioniert hat.

16.834 Beiträge seit 2008
vor einem Jahr

Ja, der Fehler ist nachvollziehbar - und richtig so.
.NET ist eine streng typisierte Sprache (wie fast alle OOP Sprachen).

Du versuchst aber im Endeffekt mit aller Gewalt Dinge in einen String zu klopfen, die halt kein String sind.


if(p.PropertyType == typeof(DateTime)
{
    if(DateTime.TryParse(value, out DateTime dt))
    {
        p.SetValue(p.Name, dt); // <-- Auch hier erhalte ich den Fehler
    }
}

dt ist DateTime, kein String. Was ist denn da die Erwartung? 😉
Die Runtime weiß einfach nicht, wie sie es zu einem String machen soll - das musst Du ihr sagen.

Ergo: jede Wert, egal welcher, musst Du vorher in einen String überführen, bevor Du den Inhalt mit SetValue reinpressen kannst.
Das kann ein ToString sein, das kann aber auch eine bewusste Überführung in String sein. Gerade bei sowas wie DateTime ( [FAQ] DateTime vs. DateTimeOffset und der Umgang mit Zeiten in .NET) lohnt sich meist die Regel von ISO8601 und damit ToString mit einem Format, hier "o".

4.939 Beiträge seit 2008
vor einem Jahr

Hallo zusammen,

der Fehler liegt in der Verwendung des falschen (ersten) Parameters von SetValue.
Dort muß das Objekt übergeben werden, nicht der Name des Properties (dieser ist ja über PropertyInfo, d.h. die Variable p festgelegt).

S
Stoffel000 Themenstarter:in
11 Beiträge seit 2021
vor einem Jahr

@ Th69,

danke für die Lösung! Da habe ich den Wald vor lauter Bäumen nicht gesehen!

Die richtige Lösung ist (falls jemand das gleiche Problem hat):


if (tabellen.TryGetValue(p.Name.ToLower(), out var val))
{
    try
    {
        Type etype = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;
        p.SetValue(obj, Convert.ChangeType(val, etype));
    }
    catch { }
}

@Abt
Der string entsteht durch das Einlesen aus einer Datei, die "wild" (kein .csv, .xml usw.) durch ein anderes Programm erstellt wird. Diese Datei wird eingelesen und die strings (die ja in diesem Fall aus der Datei als string kommen) weiterverarbeitet. Somit erhalte ich einen string der z.B. ein DateTime darstellt.
Würde es deiner Meinung nach eine bessere Alternative geben dies zu lösen? Ich bin für alles Offen und lasse mich gerne auf andere Ideen/Möglichkeiten bringen.

16.834 Beiträge seit 2008
vor einem Jahr

Ah, dann macht mein Kommentar kein Sinn. 🙂
Im Endeffekt hast Dir einen einfachen Mapper gebaut. Bei Mappern kann man viel machen; die Frage ist halt was brauchst Du davon wirklich und was reicht für den Use Case aus.
EFCore ist im Endeffekt auch nichts anderes als ein (sehr schlauer) Mapper.

2.079 Beiträge seit 2012
vor einem Jahr

Da habe ich den Wald vor lauter Bäumen nicht gesehen!

Ich glaube, da haben wir alle den Wald vor lauter Bäumen nicht gesehen - außer Th69 😁

Würde es deiner Meinung nach eine bessere Alternative geben dies zu lösen? Ich bin für alles Offen und lasse mich gerne auf andere Ideen/Möglichkeiten bringen.

Besser wäre, wenn Du auf das wilde Format verzichten könntest 😁

Aber wenn das nicht geht, ist das, was Du tust, die einzig sinnvolle Lösung.
Das kann man natürlich noch beliebig weit ausbauen, aber da bleibt natürlich die Frage, was Du brauchst und was nicht.

Und das Casten/Parsen würde ich weiterhin explizit machen, so hast Du einfach mehr Kontrolle darüber und mit dem IParsable-Interface dürfte ein Großteil der Arbeit auf einen Schlag erledigt sein.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.