Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
System.Reflection(setValue) TargetException trotz Typumwandlung
Stoffel000
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

System.Reflection(setValue) TargetException trotz Typumwandlung

beantworten | zitieren | melden

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
Fehler
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.
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Experte

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1.847
Herkunft: Düsseldorf

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
Stoffel000
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16.195

beantworten | zitieren | melden

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".
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4.494

beantworten | zitieren | melden

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).
private Nachricht | Beiträge des Benutzers
Stoffel000
myCSharp.de - Member



Dabei seit:
Beiträge: 9

Themenstarter:

beantworten | zitieren | melden

@ 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.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16.195

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Experte

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1.847
Herkunft: Düsseldorf

beantworten | zitieren | melden

Zitat von Stoffel000
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

Zitat
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.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palladin007 am .
private Nachricht | Beiträge des Benutzers