Laden...

double Eigenschaft als ref übergeben.

Letzter Beitrag vor 17 Jahren 35 Posts 3.454 Views
double Eigenschaft als ref übergeben.

Hallo,

ich habe eine Klasse mit mehreren double-Eigenschaften.

In meiner GUI steht für jede dieser Eigenschaften eine Textbox bereit.

Die Inhalte der Textboxen sollen nun die in die double-Eigenschaften der Klasse geschrieben werden.

Dabei existiert für das Umwandeln die Regel, dass der Wert nur geändert werden soll, wenn der Textboxinhalt (String) in ein double konvertiert werden kann.

Deshalb habe ich eine Methode geschrieben:

private void tryToParseDouble(ref double target, string source)
        {
            try
            {
                target = double.Parse(source);
            }
            catch(Exception) {}
        }

Bei dem Aufrufen kommt es zu einem Fehler:
Eine Eigenschaft oder ein Indexer kann nicht als out- oder ref-Parameter übergeben werden.

Jetzt frage ich mich wieso es nicht möglich ist.
Ist die Vorgehensweise nicht gut.

Alternativ könnte ich auch soetwas wie folgende Methode nutzt:


private void tryToParseDouble(double target, string source)
        {
            try
            {
                return double.Parse(source);
            }
            catch (Exception) { return target; }
        }

Dadurch würde bei dem Aufruf aber mehr Code entstehen. Das ist unwesentlich mehr, aber es geht ums Prinzip^^

Ausserdem wird dadurch der set Block der Eigenschaften aufgerufen. Dabei unterscheide ich zwar ob value!="bisheriger Wert" ist, aber ich finde es unsinnig den selben Wert erneut zuzuweisen. Ausserdem setzt diese Methode ja dann diese Prüfung in den set-Blöcken der Eigenschaften voraus.

Was denkt ihr darüber?

EDIT: Ok ich kann mir erklären, wieso es nicht möglich ist. Es kann kein Zeiger auf einen Wert einer Eigenschaft existieren. Das wäre dann eher sowas wie ein Delegate bzw. ein Methodenzeiger. Leider ist für diesen Fall die sonst gute Technik der Eigenschaft nicht zu gebrauchen.

Hallo .tim,

warum nimmst du nicht einfach double.TryParse? Damit sollten alle Probleme verschwinden.

herbivore

Oh danke.

Diese Methode sorgt für das gleiche Ergebnis wie die Methode oder❔

private void tryToParseDouble(ref double target, string source)
        {
            try
            {
                target = double.Parse(source);
            }
            catch(Exception) {}
        }

Hallo .tim,

naja, TryParse arbeitet gerade ohne Exceptions. Und es liefert einen bool zurück, ob es geklappt hat.

herbivore

Ich habe mir die Methode genauer angeschaut und diese hilft mir nicht wirklich weiter.

Ob ich nun für jede Eigenschaft den Try-Catch-Block schreibe oder ein TryParse benutze macht von der Grösse des Codes nichts aus.
Bei dem TryParse müsste ich eine Prüfung des "out Parameters" machen und je nach Wert eine Zuweisung der Eigenschaft vornehmen.

Ich suche eine Variante wie z.B. eine Methode der ich die Eigenschaft übergebe und diese gesetzt wird, wenn der übergebene String eine "Zahl" ist.

Da es sehr viele Stellen in meinem Programm habe, in dem genauso vorgegangen werden kann, hätte ich mit den beiden genannten "Methoden" sehr viel redundanz im Code.

Edit:
Der Vorteil von TryParse ist im Fehlerfall wahrscheinlich schneller da es keine Exception wirft. Das ist aber in meinem Fall eher zu vernachlässigen.

D.h. einer der folgenden Codezeilen würde sehr oft in dem Code vorkommen.
TryParse:

 double r;
            double.TryParse(source, r);
            if (r != null) target = r;

vs.
Try-Catch-Block:

try { target = double.Parse(source); }
            catch (Exception) { }
 double r;

            if(double.TryParse(source,  out r)
            {
                    // source ist eine zahl
            }

so?

Hallo .tim,

genau, im Grund läuft es auf folgendes hinaus:

if (double.TryParse (source,  out r)) { target = r; }

So besonders viel Redundanz kann ich darin nicht erkennen.

herbivore

Danke, aber die funktionsweise ist mir schon klar.

Mir geht es nur um die Grösse bzw. die Redundanz des Codes.

Beispiel:

double r;

            if(double.TryParse(source1,  out r)
            {
                    target1 = r;
            }

 if(double.TryParse(source2,  out r)
            {
                    target2 = r;
            }

 if(double.TryParse(source3,  out r)
            {
                    target3 = r;
            }

 if(double.TryParse(source4,  out r)
            {
                    target4 = r;
            }

 if(double.TryParse(source5,  out r)
            {
                    target5 = r;
            }

 if(double.TryParse(source6,  out r)
            {
                    target6 = r;
            }

 if(double.TryParse(source7,  out r)
            {
                    target7 = r;
            }

Das sieht nicht wirklich schön aus^^.

Da ist die Variante:

tryToParseDouble(ref target1, source1);
tryToParseDouble(ref target2, source2);
tryToParseDouble(ref target3, source3);
tryToParseDouble(ref target4, source4);
tryToParseDouble(ref target5, source5);
tryToParseDouble(ref target6, source6);
tryToParseDouble(ref target7, source7);

schon "sauberer". Ausserdem Zentral.
Was ist wenn aufeinmal der Kunde lieber statt dem "alten Wert" eine 0 drin stehen habe möchte. Dafür müsste ich dann nur in der Methode tryToParseDouble eine Zeile ergänzen und nicht in allen Klassen die Tryp Parse "Blöcke" suchen (können schon einpaar Hundert sein.) und diese ergänzen.

Hallo .tim,

deine Überlegungen sind ja grundsätzlich schon ok. Andererseits hast du ja auch schon richtig erkannt, warum es nicht geht, wie du es gerne hättest. Dein direkter Vergleich ist durch die unterschiedliche Formatierung auch etwas verzerrt:


tryToParseDouble(ref target1, source1);
tryToParseDouble(ref target2, source2);
tryToParseDouble(ref target3, source3);
tryToParseDouble(ref target4, source4);
tryToParseDouble(ref target5, source5);
tryToParseDouble(ref target6, source6);
tryToParseDouble(ref target7, source7);


if (double.TryParse (source1, out r)) { target1 = r; }
if (double.TryParse (source2, out r)) { target2 = r; }
if (double.TryParse (source3, out r)) { target3 = r; }
if (double.TryParse (source4, out r)) { target4 = r; }
if (double.TryParse (source5, out r)) { target5 = r; }
if (double.TryParse (source6, out r)) { target6 = r; }
if (double.TryParse (source7, out r)) { target7 = r; }

herbivore

Die Formatierung habe ich gewählt damit man trotzdem noch die Übersicht behält.

Den TryParse Block in eine Zeile unterzubringen ist unübersichtlicher als lediglich ein Methodenaufruf.

Der zweite wichtige Punkt ist die Zentralisierung.

Leider ist es aber nicht anders möglich.

Trotzdem vielen Dank für eure Hilfe und Denkanstösse.

Das Beispiel zeigt mir aber wiedermal, dass die Eigenschaften schwerwiegendere Nachteile haben, als anfangs gedacht.

D.h ich habe die Vorteile von Feldern UND Methoden aber auch die Nachteile von Feldern UND Methoden. == Mehr Vorteile und Mehr Nachteile 😉

Hallo .tim,

Die Formatierung habe ich gewählt damit man trotzdem noch die Übersicht behält.

also normalerweise formatiere ich ein if auch über mehrere Zeilen, aber in diesem speziellen Fall finde ich es deutlich übersichtlicher nur eine Zeile pro if zu verwenden. Aber das ist natürlich Geschmackssache.

herbivore

Wenn die Länge der Eigenschaften: source und target ungefähr genauso lang sind finde ich es auch deutlicher. Bei Grössenunterschieden von 30 Zeichen empfinde ich es wie ein Fliesstext^^

Aber wie du bereits auch angemerkt hast, jeder mag es anders 🙂

Wieso machst Du es nicht so:

Eine eigene TryParseOrOriginal-Methode

In:
string pNewValue
double pOldValue

Return:
Result


public static double TryParseOrOriginal(double pOldValue, string pNewValue)
{
   double ret=pOldValue;
   try
   {
      ret=double.Parse(pNewValue);
   }
   catch{} //forget it
   return ret;
}

Aufruf:

myDouble=TryParseOrOriginal(myDouble, textBoxXY.Text);

Das ganze ist ungetestet müsste aber funzen.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

Meine Eigenschaft soll ich dann entsprechend der Rückgabe der Methode setzen?

Dann würde der set-Block der Eigenschaft auch ausgeführt werden, wenn es sich um das Selbe handelt.

D.h ich muss sicherstellen das nur Eigenschaften über diese Methode laufen die entweder im set-Block nur einen Wert einer Variable zuweisen oder das eine Prüfung (value!="bisheriger Wert") stattfinden muss.

Da ich das nicht ausnahmslos Sicherstellen kann, ist die Variante nicht machbar.

Wenn ich eine Überprüfung im Code einbaue stehen wir wieder am Anfang mit der Redundanz und damit verbundenen Problemen.

Trotzdem vielen Dank.

Da ich das nicht ausnahmslos Sicherstellen kann, ist die Variante nicht machbar.

Naja, wenn du Mist verarbeitest kommt selten mehr als Mist raus...

Wenn du nicht an die Felder selber rankommst, dann geht das was du willst so nicht, aus dem einfachen Grund, weil der Getter der Eigenschaft ja schon mal gleich den Wert kopiert und du dann nur noch die Kopie referenzieren kannst...

Du kannst natürlich auch das Zielobjekt (die Instanz der Klasse) und den PropertyNamen übergeben.

Innerhalb der Methode ziehst Du Dir dann (falls der Wert geparst werden kann und sich auch verändert hat) ein PropertyInfo und setzt den Wert direkt.


public static void SetOrForget(object pTarget, string pPropertyName, double pOldValue, string pValueToParse)
{
   try
   {
      double newValue=double.Parse(pValueToParse);
      if (newValue!=pOldValue)
      {
         PropertyInfo pi=pTarget.GetType().GetProperty(pPropertyName);
         pi.SetValue(pTarget,newValue,new object[]{});
      }
   }
   catch{}
}

und auch dies ist nicht getestet.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

x=x ist meiner Meinung nach "Mist".
Das man bei dem set-Block von Eigenschaften nicht sicherstellen kann, das diese beim selben Inhalt keine Befehle ausführen, ist meiner Meinung nach trivial.

Die Referenz soll nicht auf das Feld, das ggf. in der Eigenschaft zugewiesen werden soll, existieren. Sondern auf die Eigenschaft, welche ja wie ich bereits im ersten Beitrag geschrieben habe, eher ein Delegate bzw. ein Funktionszeiger ist.

Meine Idee wäre ja für dieses Problem eine "virtuelle Referenz" welche 2 Delegates enthält, wobei eines für set und das andere für get zuständig ist.

Ob dies eine sinnvolle Umsetzung oder zu voreilig vorgeschlagen ist, müsste in einer längeren Diskussion besprochen werden.

Vielen Dank Programmierhans, an Reflektions habe ich garnicht gedacht. Das werde ich mir mal durch den Kopf gehen lassen 🙂

Naja, Exceptions im Fehlerfall und der Einsatz von Reflection machen die Sache aber ziemlich unperformant...

Wenn überhaupt mit Reflection, dann spar dir wenigstens die Exceptions im Fehlerfall:

public static void SetOrForget(object pTarget, string pPropertyName, double pOldValue, string pValueToParse)
{
   double newValue
   if (double.TryParse(pValueToParse, out newValue)
   {
      if (newValue!=pOldValue)
      {
         try
         {
         PropertyInfo pi=pTarget.GetType().GetProperty(pPropertyName);
         pi.SetValue(pTarget,newValue,new object[]{});
         }
         catch{}
      }
   }
}

Wenn du's über Reflection lösen willst, dann wären aber warscheinlich eher dynamische Methoden das Mittel der Wahl...

Ich werde TryParse und Reflektions benutzen.

Danke für eure Hilfe.

EDIT: Meinst du mit "dynamische Methoden" "generische Methoden"?

Hallo .tim,

nein, wohl eher die DynamicMethod-Klasse.

herbivore

Das sagt mir leider garnichts.
In der Hilfe steht leider nur, dass DynamicMethod eine dynamische Methode darstellt^^

Kann mir jemand 1-2 Sätze dazu sagen. Für was benötige ich das, bzw. was kann ich damit machen?

Vielen Dank.

Hallo .tim,

Kann mir jemand 1-2 Sätze dazu sagen.

die Forensuche liefert dir bestimmt mehr als 1-2 Sätze dazu. Bitte die besten Treffer hier posten. Vielen Dank!

herbivore

Also ehrlich - exceptions, reflection, dynamische Methoden. Ich würde eine ganz einfache Variante von Programmierhans Idee verwenden:


public static double Parse(string data, double defaultValue) {
	double parsed;
	return double.TryParse(data, out parsed) ? parsed : defaultValue;
}

Verwendung (wie von Programmierhans vorgeschlagen):


  obj.Eigenschaft = Parse(txtBox.Text, obj.Eigenschaft);

Das ist schnell, einfach, intuitiv und vielseitig einsetzbar (z.B. andere Default-Werte). Und ob der Eigenschaft nun ihr ursprünglicher Wert zugewiesen wird (x=x) ist doch vollkommen egal. In welchem Szenario soll sich diese Situation denn abspielen, dass es problematisch ist? Das Parsen des Strings dauert bereits ein vielfaches länger.

Alles andere halte ich angesichts der Problembeschreibung für - sorry - overkill und großen Humbug (vom Lehreffekt mal abgesehen).

Grüße,
Andre

P.S.: Der setter einer Eigenschaft sollte grundsätzlich nicht zu viel machen. Eigenschaften vermitteln den Eindruck einer gewissen "Leichtigkeit". Wenn eine Eigenschaft "sehr viel" machen muss, ist es psychologisch sinnvoller, eine Methode daraus zu machen.

Original von herbivore
Hallo .tim,

Kann mir jemand 1-2 Sätze dazu sagen.
die Forensuche liefert dir bestimmt mehr als 1-2 Sätze dazu. Bitte die besten Treffer hier posten. Vielen Dank!

herbivore

Leider habe ich nichts hilfreiches gefunden.

Original von VizOne
Alles andere halte ich angesichts der Problembeschreibung für - sorry - overkill und großen Humbug (vom Lehreffekt mal abgesehen).

Danke, vielleicht hast du Recht und ich habe mich auf die "SetOrForget-Methode" festgefahren.

Was die dynamischen Methoden angeht, da findest du bei http://www.codeproject.com ein paar nützliche Artikel.

Prinzipiell sehe ich das aber genauso wie VizOne (auch wenn der Weg über dynamische Methoden kaum Performanceverluste bedeuten würde). Ja, da entstehen Redudanzen, aber die entstehen in jedem Fall, weil selbst wenn du alles in eine supertolle Methode auslagern kannst, musst du immernoch irgendwie sagen, auf welches Feld du zugreifen willst und die Zeichenkette muss natürlich auch mit übergeben werden.

Danke, eine Quelle in Deutsch wäre mir zwar lieber....(Wörterbuch such)...aber trotzdem danke 🙂

Hallo zusammen,

P.S.: Der setter einer Eigenschaft sollte grundsätzlich nicht zu viel machen. Eigenschaften vermitteln den Eindruck einer gewissen "Leichtigkeit". Wenn eine Eigenschaft "sehr viel" machen muss, ist es psychologisch sinnvoller, eine Methode daraus zu machen.

vor allem sollte ein Setter dann gar nichts (aufwändiges) machen, wenn sich durch das erneue Setzen des gleichen Werts nichts ändert. Viele Properties der Frameworkklassen, die beim Ändern des Wertes einen Event auslösen, enthalten z.B. so eine Abfrage.

Hallo .tim,

Leider habe ich nichts hilfreiches gefunden.

Das liegt dann aber an dem fehlenden Willen, etwas zu finden, oder? Der fünfte Treffer der sehr übersichtlichen Trefferliste enthält im ersten Beitrag einen Link auf den Abschnitt "How to: Define and Execute Dynamic Methods" der MSDN-Doku. Was willst du mehr??? OK, du willst es vermutlich auf Deutsch, ein Problem. Einfach in dem Link das /en-us/ in /de-de/ ändern. Also alles eine Frage der Eigeninitiative, die wir gerade, wenn es um Begriffsklärungen geht, erwarten.

herbivore

Hallo,

den Artikel How to: "Define and Execute Dynamic Methods" habe ich gelesen, dadurch habe ich teilweise verstanden was eine dynamische Methode ist. Da es bei dem Artikel um das "Wie" ging, weiss ich nun wie man diese Methode erstellt. Leider ist mir dadurch unklar für was ich das in meinem Fall gebrauchen kann. Darum ging es mir in meiner Frage. Was kann ich damit machen bzw. was ist eine dynamische Methode. Ich habe auf verschiedenen Seiten und in der Forensuche nach einer Beschreibung gesucht, leider kein Erfolg.

Mein Wille ist da, für die Suche was überhaupt eine dynamische Methoden ist.

Jemand der kein Auto kennt, würde um es für sich definieren zu können, die Antwort ein Fortbewegungsmittel mit 4 Rädern helfen. Stattdessen findet er einen Artikel über "Wie fahre ich Auto?". Diese Person wird auch erstmal versuchen auf seine "einfache" Frage eine Antwort zu finden.

Bei Properties bist du der Meinung, dass bei dem set-Block, sowas wie folgendes enthalten sein muss?

set
{
if(value!=kunde)
{
kunde = value;
// weiterer Code
}
}

Ist das nur deine Meinung oder ist das Pflicht beim Entwickeln von Eigenschaften?

Danke
.tim

Hallo .tim,

Bei Properties bist du der Meinung, dass bei dem set-Block, sowas wie folgendes enthalten sein muss?

wenn die Property den übergebenen Wert ohne weiteres in ein Feld schreibt, kann man sich die Prüfung natürlich sparen. Pflicht ist das ohnehin nicht, allenfalls eine Konvention, dass beim Setzen des gleichen Werts kein ...Changed-Ereignis ausgelöst wird, eben weil sich ja gar nichts geändert hat.

herbivore

Ich finde den Begriff dynamische Methode eigentlich selbst erklärend, es handelt sich dabei um eine Methode, die dynamisch - also zur Laufzeit - erzeugt wird. Aus diesem Grund kann eine solche Methode auch nur als Delegate aufgerufen werden, weil man sie ja zur Compilezeit noch nicht kennt.

Aber das ist jetzt eigentlich was das Thema betrifft ja nur noch Hintergrundwissen. Das komplette Thema dynamische Methoden hiere in einem Thread abzuhandeln, aus dessen Titel man nie erahnen würde, dass da dynamische Methoden drinstecken (weil's eigentlich Blödsinn ist, sowas mit dynamischen Methoden machen zu wollen, die Musterlösung ist die, die im letzten Beitrag von VizOne steht), fänd ich blödsinnig, weil es dem Grundgedanken eines Forums (neue Themen in neue Threads) m.E. widerspricht (ehrlich gesagt würd ich mich nicht wundern, wenn demnächst einer der Admins die letzten Beiträge abtrennt)

Original von onlinegurke
..., es handelt sich dabei um eine Methode, die dynamisch - also zur Laufzeit - erzeugt wird. Aus diesem Grund kann eine solche Methode auch nur als Delegate aufgerufen werden, weil man sie ja zur Compilezeit noch nicht kennt. ...

Genau sowas hatte ich gesucht, danke.

Hallo .tim,

warum dann die ganze Diskussion? In der Hilfe, in die du angeblich geschaut hast, steht doch genau das:

Sie können mit der DynamicMethod-Klasse eine Methode zur Laufzeit generieren und ausführen

herbivore

Ich meinte damit eher. Sowas wie eine kurze Beschreibung habe ich gesucht. Das sollte nicht bedeuten, dass ich die Beschreibung von onlinegurke ausreichend, dass aber auch nicht mehr nötig ist, finde.

Man kann sich unter Erklärungen wie

Sie können mit der DynamicMethod-Klasse eine Methode zur Laufzeit generieren und ausführen

nicht wirklich etwas vorstellen. Jetzt kommen bestimmt wieder Anmerkungen das irgendjemand mit dem Satz mehr anfangen kann.

Ich möchte mich trotzdem für die sonstige Hilfe und Vorschläge bei allen Bedanken. Die Diskussion können wir von mir aus hiermit beenden.

Hallo .tim,

es war als rhetorische Frage gemeint. Ich wollte die Diskussion nicht weiter verlängern und hatte darauf keine Antwort erwartet.

In der Dokumentation steht ja weit mehr als dieses einleitende Satz.

Ich will darauf hinaus, dass du bitte solche grundlegende Begriffe selber klärst und damit nicht das Forum beschäftigst. Das Forum ist für konkrete Fragen und Problem, nicht dafür Begriffsdefinitionen zu geben. Dafür gibt es besser Quellen, im allgemein MSDN, Wikipedia und oft auch Google. Bitte berücksichtige das in Zukunft.

herbivore

PS: Eine weitere Antwort ist nicht erforderlich.