Entwickler-Ecke

C# - Die Sprache - getter/setter Modifikation zum abfangen ungültiger Werte


Glowhollow - Di 16.10.18 18:08
Titel: getter/setter Modifikation zum abfangen ungültiger Werte
Hallo zusammen,

wie ihr richtig erratet habt, habe ich da eine Frage.

Um es gleich vorwegzunehmen, fragt bitte nicht, warum ich den NewtonSoft JSON-Parser nicht genommen habe. Das ist leider nicht möglich. Ich habe einen eigenen JSON-Parser mit Deserializer geschrieben, der funktioniert soweit auch ganz gut. Kann man mit arbeiten.

Jetzt habe ich diverse Datensätze und habe hier ein kleines Problem.

Ich habe eine Property, die .link und .value annehmen kann. Aber, das ding kann auch leer sein, bzw. null.

Ich habe hierzu den getter und setter angepasst. Hier ein Auszug aus dem Code.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
private ResolvedBy myVar1;
public ResolvedBy resolved_by
{
 get
 {
  if (myVar1 == null)
   {
   myVar1 = new ResolvedBy();
   return myVar1;
   }
   // if (String.IsNullOrWhiteSpace(myVar1.link) && String.IsNullOrWhiteSpace(myVar1.value))
   if (myVar1.ToString() == String.Empty)
   {
   myVar1 = new ResolvedBy();
   return myVar1;
   }
   return myVar1;
  }
  set { myVar1 = value; }
}


desweiteren


C#-Quelltext
1:
2:
3:
4:
5:
public class ResolvedBy //
{
public string link { get; set; }
public string value { get; set; }
}


Sollte in diesem Fall jetzt unerwarteterweise ein Leerstring, statt .link und .value stehen, sollte eigentlich die Klasse neu instantiert werden.

Ich gehe aber davon aus, das zwar die Properties erstellt werden, jedoch diese null sind.

Wie verhindere ich, bzw., wie löse ich das Problem, das beim abfragen von link und value keine null exception passiert ?

Ich müßte die Felder mit Leerstrings füllen, aber wie würde die Funktion dazu aussehen ?


Ralf Jansen - Di 16.10.18 18:45

Kommt jetzt ein wenig drauf an wie dein Serializer funktioniert und ob ein normales instanzieren deiner Klasse auch so funktionieren soll;)

Unter der Annahme das es sich gleich Verhalten soll und dein Serializer auch den Konstruktor der Klasse benutzt (und nicht daran vorbei funktioniert) würde ich die entsprechenden Properties schon leer vorbelegen. Der Serializer wird die dann schon im Zweifel überschreiben wenn etwas entsprechendes im JSON vorhanden ist. Wenn dein Serializer der Property explizit null zuweißt wenn die nicht im JSON enthalten ist (keine Ahnung wie man das programmiert haben sollte) solltest du deinen Serializer fixen.

Also ganz simpel einfach


C#-Quelltext
1:
2:
private ResolvedBy myVar1 = new ResolvedBy();
public ResolvedBy resolved_by { get => myVar1; set => myVar1 = value; }



Zitat:
Sollte in diesem Fall jetzt unerwarteterweise ein Leerstring, statt .link und .value stehen, sollte eigentlich die Klasse neu instantiert werden.


Sollte da tatsächlich ein Leerstring stehen (anstatt das resolved_by komplett fehlt) muss das meiner Meinung nach knallen. Deine Property ist vom Typ ResolvedBy und ein Leerstring ist nie vom Typ ResolvedBy. Ein übersetzen von Leerstring nach ResolvedBy kann nicht erst in der Property passieren sondern wenn man das will (und das sollte man nicht wollen) muss das schon im Serializer passieren. Wenn deine Property strings und ResolvedBy zulassen soll bleibt dir ja fast nur übrig die als object zu definieren und dann im Property Setter Typprüfungen vorzunehmen.

Zitat:
Ich müßte die Felder mit Leerstrings füllen, aber wie würde die Funktion dazu aussehen ?


Das verstehe ich nicht bzw. solltest du nochmal näher erläutern. Der Zugriff auf link oder value wird keine Exception auslösen wenn die null sind. Nur wenn du versuchst daran noch eine Methode aufzurufen.

Zitat:
Um es gleich vorwegzunehmen, fragt bitte nicht, warum ich den NewtonSoft JSON-Parser nicht genommen habe. Das ist leider nicht möglich. Ich habe einen eigenen JSON-Parser mit Deserializer geschrieben, der funktioniert soweit auch ganz gut. Kann man mit arbeiten.


Empfehlen würde ich dir das trotzdem. Wenn du was eigenes machst weil du glaubst die andere Lösung ist zu komplex und du willst was einfacheres sei dir gesagt du wirst bei einer Lösung landen dir nur deine speziellen eigenen JSONs erstellen/lesen können wird. Wenn du beliebieges JSON damit lesen können willst wirst du bei der gleichen Komplexität landen. Du hast diese Komplexität dann zwar möglicherweise besser verstanden das ganze wird aber wesentlich schlechter getestet sein als der Code der bereits von 99% aller Entwickler die sich mit JSON rumschlagen benutzt wird.


Glowhollow - Di 16.10.18 19:16

Hallo Ralf !

Danke für deine Ausführungen, das ganze hat mich auf eine Idee gebracht.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
private ResolvedBy myVar1;
public ResolvedBy resolved_by
{
   get
   {
                    
   if (myVar1 == null)
   {
   return new ResolvedBy(); 
   }
   if (String.IsNullOrWhiteSpace(myVar1.link) && String.IsNullOrWhiteSpace(myVar1.value))
   {
   return new ResolvedBy { link = "", value = "" };
   }
  return myVar1;
 }
 set { myVar1 = value; }
}


Ich bin jetzt damit schon einen Schritt weiter. Ich kriege schon mal keine nullreferenz mehr, aber habe nun folgendes Problem. Ich habe ingesamt 53 Felder mit 83 Properties. Bis Feld 23 deserialisiert er sehr gut und auch mit dem null Value in Feld 23 kommt er gut klar.

Man sollte davon ausgehen, das er Feld 24, korrekt befüllt, statt dessen, obwohl werte vorhanden sind, schreibt er ab feld 23 nur noch leerstrings.

Hat mir jemand eine Erklärung dafür ?

Edit: Ich habe da eine vage Vermutung. Da ja im getter/setter eine neue Instanz von ResolvedBy erstellt wird, weiß, so vermute ich, der Deserializer ab dem Moment nicht mehr, wohin er die Daten schreiben soll ? Daher, bleibt der Rest leer ? Kann das sein ?


Ralf Jansen - Di 16.10.18 21:26

Wir können mit dem wenig Wissen das wir über deinen Code haben nur spekulieren.
Deine Vermutung würde ich eher verneinen. Ich würde nicht erwarten das ein Deserializer den Getter braucht. Bei den Implementierungen die ich mir vorstellen kann wird nur der Setter verwendet und nie der Getter.

Aka beim einlesen der Textquelle wird der entsprechende Knoten erkannt. Hier zum Beispiel das "resolved_by". Der Deserializer wird rausfinden das die entsprechende Property vom Typ ResolvedBy ist. Jetzt rekursiv den Deserializer für diesen Typen aufrufen. Heißt instanziiert eine Object vom Typ ResolvedBy, schreiben der Daten in dieses Object und dann zuweisen dieses Objects an die Property. Das wäre dann ein Aufruf des Setter aber nie ein Aufruf des Getters. Für das Deserialisieren sollte also der Getter egal sein da er nicht benutzt wird.

Aber wie gesagt das ist alles nur Spekulation die nicht hilft. Wieso debuggst du deinen Code nicht einfach mal und schaust was der den tut anstatt uns spekulieren zu lassen? Wenn du das tust lass dich dann nicht tölpeln ;) Wenn du mit dem Debugger nachschaust was in der Properties so steht wir deren Getter benutzt und deine Lazy Initialisierung ausgeführt. Auch ein Grund warum ich das initialisieren da rausgezogen habe. Das Verhalten ist wesentlich einfacher durchschaubar ohne in den meisten Fällen schlechter zu sein.


Glowhollow - Do 18.10.18 11:16

huhu, du hast natürlich recht, der getter ist hier wenig hilfreich. Ich müßte den Setter modifizieren. Nur funktioniert dies hier leider nicht...


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
private ResolvedBy myVar1;
public ResolvedBy resolved_by
{
 get
 {
  if (myVar1 == null)
  {
  myVar1 = new ResolvedBy();
  return myVar1;
  }
  if (String.IsNullOrWhiteSpace(myVar1.link) && String.IsNullOrWhiteSpace(myVar1.value))
  {
  myVar1 = new ResolvedBy();
  return myVar1;
  }
  return myVar1;
 }
 set
 {
  if (value == null)
  {
   myVar1.link = "";
   myVar1.value = "";
  }
    myVar1 = value;
 }
}


Ich bekomme einen "Ein Aufrufziel hat einen Ausnahmefehler verursacht". Kann man dies evtl. verhindern ?


Th69 - Do 18.10.18 12:35

Bei einer TargetInvocationException schau in die InnerException, um die genaue Fehlerursache zu erhalten.


Ralf Jansen - Do 18.10.18 12:39

Zitat:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
set
{
    if (value == null)
    {
        myVar1.link = "";
        myVar1.value = "";
    }
    myVar1 = value;
}


Warum sollte myVar1 ein Objekt enthalten und nicht null sein wenn value null ist? Bedenke wir haben schon bemerkt das der getter voraussichtlich nicht benutzt wird.
Und warum schreibst du in myVar1 erst was rein wenn value null ist um nachher die null in myVar1 reinzuschreiben womit jede vorherige Änderung weg ist?


Glowhollow - Do 18.10.18 13:33

Hmpf, fehler beim kopieren gemacht.

Das ist natürlich quatsch...


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
 set
 {
 if (value == null)
 {
 myVar1 = new ResolvedBy();
 myVar1.link = "";
 myVar1.value = "";
 }
 else
 {
  myVar1 = value;
 }
}


ich muß beim setzten prüfen, ob das value das gesetzt werden soll ist null ist, wenn ja, fülle link und value mit leerstrings, erstelle evtl. ein neues objekt. Ist das so richtig ?


Ralf Jansen - Do 18.10.18 14:59

Das macht zumindest mehr Sinn ;) Ob das letztlich richtig ist kann ich ohne mehr Context nicht wirklich beantworten. Könnte funktionieren und deinen Wünschen entsprechen.


Allgemein finde ich aber weiterhin das Verhalten fraglich. Wenn es etwas nicht gibt soll es gefälligst null sein und nicht irgendein Standardwert darstellen. Null und Leerstrings sind verschiedene Dinge und das nicht grundlos.


Glowhollow - Do 18.10.18 16:14

Ohh Mann, ich bin bescheuert.

Die Datensätze ab Datensatz 23, haben tatsächlich keine weiteren Werte mehr für diese Property, auch nicht in den folgenden 30 Datensätzen. Kann also zugemacht werden, läuft jetzt.

Liebe Grüße