Mein Projekt: Formelparser und Funktionsplotter

  • VB.NET
  • .NET (FX) 4.0

Es gibt 59 Antworten in diesem Thema. Der letzte Beitrag () ist von Carbonunit.

    Also gut, ich gucke mir das mal genauer an mit den Unit-Tests. Aber im Moment geht es darum, zu sehen, ob mein Parser mit irgendwelchen Terms richtig rechnet. Da benutze ich eben Excel per Automation, um meinen Term zu übergeben. Excel kann das ausrechnen und das Ergebnis kann ich vergleichen und dann weiß ich, ob mein Parser richtig rechnet.
    Die Sache mit den Unit-Tests sieht sehr viel aufwändiger aus und ich hab keine genaue Ahnung, was die machen, weshalb ich das ja auch gar nicht ablehnen will, aber für den momentanen Zweck tut Excel genau das, was es soll, nämlich mir anzeigen, ob mein Ergebnis stimmt oder nicht.
    Ist einfach:

    VB.NET-Quellcode

    1. <TestClass()> Public Class UnitTest1
    2. Dim pr As ConsoleApp1.Parser = New ConsoleApp1.Parser()
    3. <TestMethod()> Public Sub CheckMultiply0()
    4. Assert.AreEqual(0, pr.calculateStuff("10*0"))
    5. End Sub
    6. End Class
    Bilder
    • Image 2017-08-06-001.jpg

      200,91 kB, 985×343, 114 mal angesehen

    8-) faxe1008 8-)
    jo, und dann noch paar annere Samples:

    VB.NET-Quellcode

    1. Assert.AreEqual(15.0, pr.calculateStuff("(3 + 2) * (15 / 3 - 8)"))
    2. Assert.AreEqual(-15.0, pr.calculateStuff("(3 + 2) * (15 / !3 - 8)"))
    3. Assert.AreEqual(-13.0, pr.calculateStuff("(3-4^2)*1"))
    Also wenn das durchläuft, ist schon sehr gutt.

    @Faxe: Problem ist aber erstmal ühaupt den Shunting-Yard zu testen

    (Und ausserdem: So Test-Methoden kann man sich auch so erstellen, und einfach in Sub Main durchlaufen lassen. Einfach Messagebox ausgeben, wenn fail)
    Moin zusammen,

    der Witz ist ja, das richtige Ergebnis nicht erst selbst mühsam auszurechnen, sondern dafür Excel herzunehmen, das das ja schon kann. Das muss auch erstmal bei niemand sonst laufen, nur bei mir. Ich hab mir eine Form gebaut, in der das alles getestet wird. Wenn das Ergenbis stimmt, wird die Textbox grün, sonst pink. Und seit ich die englischen Original Excel-Funktionen für Arcustangens (ATAN satt ARCTAN) und Wurzel (SQRT statt WURZEL) verwende, kann es die auch auswerten. Die muss ich nur in dem Term für Excel kurz austauschen.
    @ErfinderDesRades: Mit deinen Formeln läuft es durch und rechnet auch richtig, puh... Der Rangierbahnhof scheint zu funktionieren.
    Hallöchen,

    mal wieder eine Nachricht aus meiner rollenden Coding-Höhle.
    Mittlerweile steht das Grundgerüst für den Funktionsplotter. Darin arbeite ich viel mit den "List(Of T)" Elementen, Konstrukten oder wie immer man das nennen soll.
    Es gibt eine Liste der Funktionen, die der Funktionsplotter bearbeiten und anzeigen kann (maximal 16, also mit Indizes von 0 bis 15) mit den Namen FXA bis FXP. Jede dieser Funktionen hat eine Laufvariable und bis zu 15 Konstanten, deren Werte ich vor der ersten Berechnung einsetze. Die sind natürlich auch in einer List(Of T), ebenso wie die Tokens des Terms in InFix- und PostFix-Notation.

    Dann wird es die Möglichkeit geben, einzelne Funktionen aus dem Funktionsspeicher zu verknüpfen.
    Beispiel: Die Funktion heißt FXA und beinhaltet einen Sinus: "Amp*Sin(f*t+Phi)", die andere heißt FXB und beinhaltet dieselbe Formel, nur mit einem anderen Wert für Phi. Wenn man beides multipliziert und als f(t) zeichnet, zeigt das wie elektrische Blindleistung entsteht. Das Produkt aus Spannung und nicht exakt phasengleichem Strom wird nämlich zeitweise negativ. Und negative Leistung ist keine. Aber das nur am Rande.
    Im Funktionsspeicher unter dem Namen FXC kann ich jetzt FXA * FXB eingeben und das zeichnen lassen. Das ist noch nicht fertig, sonst gäbe es hier mindestens einen Screeshot, aber das ist der Plan.

    Ebenso gibt es Listen für die offenen Fenster und für jedes Fenster wiederum eine Liste der darin bereits gezeichneten Funktionen.
    Jetzt zu meiner Verständnisfrage: Ich habe eine List(Of String) für jedes Fenster angelegt, in der ich den Namen des Funktionsspeicherplatzes eintrage, dessen Funktion in dieses Fenster gezeichnet hat. Steht der Name schon drin, soll er nicht nochmal eingetragen werden. Dafür wolte ich die Exists-Methode benutzen, aber dafür muss ich einen "Predicate(Of T)" Delegaten bauen, der dann über AddressOf aufgerufen wird etc. So sagt es zumindest die Hilfe.
    Da kann ich auch selbst durch die Liste blättern und sehen, ob der String schon drin ist oder nicht. Oder wo ist dabei der Vorteil des List(Of T Konstrukts und seiner Exists-Methode?
    Die Add- und Remove-Methoden sind tatsächlich nützlicher, als das bei einem Array alles selbst machen zu müssen, aber bei Methoden wie Exists muss ich ja immer noch die Mimik bauen.

    Gruß
    Carbonunit
    Statt Exists("bla") benutze einfach IndexOf("bla") >=0, wenn du den Predicaten-Kram nicht haben willst.
    Ist tatsächlich vonne Architektur her komisch,dasses keine Exists-Überladung ohne predicate gibt.
    Wenn wolle kannste dir auch eine eigene .Exists - Extension dafür schreiben, die dann auf IndexOf >= 0 umleitet, aber kannste auch lassen.
    Dafür eine List zu verwenden ist der falsche Ansatz.
    Terme lassen sich induktiv definieren, sind also perfekt geeignet um mittels einer baumstruktur (=induktive datenstruktur) dargestellt zu werden.
    Das was der shunting yard algorithmus dann macht, nennt man auch postorder traversierung.

    Bei Listen hast du nämlich das Problem dass du jeden Token für sich betrachtest (indem du durch die liste iterierst), aber um eine funktion auswerten zu können (in welcher form auch immer) braucht es die Möglichkeit eine Zahl z.B. in ihrem Kontext zu betrachten (z.B. 3 kann sowohl 2^3 als auch 3+2 oder 3*(6+1) sein).

    Wenn du bspweise die Ableitung einer funktion bilden möchtest, ist diese auch induktiv definiert undzwar entweder ist f (x) zusammengesetzt aus g (x) und h (x) (g (h(x)) oder g (x)*h (x) oder g (x)+h (x)) und du kannst mit f (x) = g (x) und f (x) = h (x) den induktionsschritt machen(Fallunterscheidung und anschließend rekursiver funktionsaufruf an kinderbäume) oder f (x) ist elementar d.h. die potenzregel ist anwendbar.

    Mit einer liste ist sowas nur schwer umsetzbat
    @ErfinderDesRades: Jawoll, das hat funktioniert. Als Ergänzung, weil ich das nicht in der Hilfe gefunden habe: IndexOf gibt -1 zurück, wenn das Argument nicht in der Liste gefunden wird.

    @RushDen: Ich habs jetzt so hingekriegt mit den Listen, dass der Term verarbeitet wird, in PostFix-Notation umgestellt wird und auch richtig berechnet wird. Vielleicht hab ich mir damit mehrfach ins Knie geschossen, aber es funktioniert jetzt.
    Das mit den Bäumen und Verzweigungen versuche ich später mal. Muss ich auch noch genauer verstehen, ehrlich gesagt.
    Es stimmt natürlich schon, dass man bei einer Liste immer sehen muss, in welcher Beziehung die Tokens zueinander stehen. Ist das Minus jetzt ein Operator oder das Vorzeichen, um mal das gemeinste Beispiel zu nennen.
    Die PostFix-Notation erleichtert die Verarbeitung aber enorm. Für meinen Funktionsplotter wird der Term in InFix-Notation eingegeben, dann per Shunting-Yard Algorithmus in PostFix-Notation einmal umsortiert und dann nach einem ziemlich simplen Schema mehrfach mit immer neuen Werten für x berechnet.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Carbonunit“ ()

    Hallo,

    es geht hier natürlich weiter, nur war ich in letzter Zeit anderweitig beschäftigt. An meinem Projekt habe ich hier und da ein bisschen gebastelt, aber nichts Substanzielles bisher. Das soll jetzt anders werden.
    Ich habe aber, immer wenn es etwas zu Berechnen gab, meinen Formelparser benutzt und der hat sich bisher noch nicht verrechnet.

    Zum besseren Testen muss ich ja meine Funktion f(x) speichern und wieder laden können, ebenso die Einstellungen meines Funktionsplotters. In der alten VB6-Version davon, die zu ihren Glanzzeiten mit Makrosprache inklusive Debugger daherkam, habe ich alles als INI-Dateien gespeichert. Dazu hatte ich mir einen Wrapper um die INI-Funktionen gebastelt (WritePrivateProfileString und Konsorten). Natürlich war das damals schon veraltet, Windows schleppt die API nur noch aus Kompatibilitätsgründen mit sich herum (oder weil sie in Redmond nicht wissen, was alles noch kaputt geht, wenn sie es rausschmeißen... ;) ).

    Jetzt will ich serialisieren. Das sagt sich nur so schön, hab ich glaub ich noch nie gemacht. Nach etwas MSDN lesen denke ich, dass es etwa so funktionieren könnte:
    Die Daten, die ich serialisieren will, sind als Public-Eigenschaften in einer Klasse definiert, also in Pseudocode etwa so:

    VB.NET-Quellcode

    1. Public Class Function
    2. ' Term der Funktion in InFix-Notation
    3. Public Term as String
    4. ' Symbole, wie das "A" oder das "x" im Term "A*x^2"
    5. Public Class OneSymbol
    6. Public SymbolName As String
    7. Public SymbolValue As Double
    8. Public IsControlVar as Boolean ' True, wenn Laufvariable
    9. End Class
    10. ' Jeder Term kann bis zu 16 (0..15) Symbole haben
    11. Public Symbols(15) As OneSymbol
    12. End Class


    Die Daten liegen natürlich auch in meiner Formelparserklasse vor, nur da ist noch alles Mögliche andere mit dabei, wie private und öffentliche Methoden oder Daten, die nicht gespeichert werden müssen. Darum die Überlegung, diese Daten in so eine Klasse zu kopieren und das dann zu serialisieren. Aus dem so gespeicherten XML kann ich beim Einlesen (Deserialisieren) das Objekt mit genau dem Inhalt wieder aufbauen, den Inhalt in meine Formelparserklasse kopieren und loslegen.

    Ähnliches dann auch für die Einstellungen meines Funktionsplotters, da gibt es schon ein schickes Dialogfenster mit Tabcontrol.
    Ist das so das Mittel der Wahl oder gibts da noch was Besseres?

    Wenns läuft, gibts Beispielcode und so...

    Gruß
    Carbonunit
    joa, Serialisierung ist wohl eine gängige Vorgehensweise für sowas.

    ich würde nur den Klassen-Namen Function überdenken, weil das gibts bereits, und ist ein Schlüsselwort. Ist Function wirklich die Bezeichnung, die am allerbesten beschreibt, was diese Klasse darstellt?

    Etwas wundert mich auch, was es da vieles abzuspeichern gibt - eine Funktion lässt sich doch einfach als String formulieren, und feddich - du hast ja einen Parser dafür.
    Ja klar, war ja auch Pseudocode und heißt jetzt ValfFunction. Der Term kann Konstanten und Variable enthalten (ich nenn die Symbole), wie z.B. A*x^2+B*x. Da muss der Parser die Werte für A, B und x kennen, wobei x in dem Fall die Laufvariable ist. Aber auch das muss der Parser wissen, darum das ganze Zeugs da drumherum. Gerade hab ich es zum Fliegen gekriegt mit Serialisierung und Deserialisierung. Ist wahrlich keine Raketenwissenschaft und ich hab wieder was gelernt.
    Hallöchen,

    hier die offizielle Erfolgsmeldung mit ein bisschen Beispielcode.
    Folgende Klasse habe ich definiert zur Aufnahme der Daten einer Funktion, wie sie mein Matheprogramm verwenden soll:

    VB.NET-Quellcode

    1. Public Class ValfFunction
    2. ' Serialisierbare Version einer Funktion, wie sie mein Funktionsparser Valf verarbeitet
    3. ' Der Term in InFix-Notation
    4. Public ValfTermInFix As String
    5. ' Anzahl tatsächlich definierter Symbole
    6. Public SymbolsCount As Integer
    7. ' Symbol-Definition
    8. ' Symbole sind z.B. im Term A * x ^ 2 + B * x die Zeichen A, B und x
    9. ' Diese haben einen Namen, einen Wert und können die Laufvariable sein, wie in diesem Beispiel das x
    10. Public Structure OneSymbol
    11. Public SymbolName As String
    12. Public SymbolValue As Double
    13. Public IsControlVar As Boolean
    14. End Structure
    15. ' Liste der Symbole als Array, wird zur Laufzeit dimensioniert
    16. Public Symbols() As OneSymbol
    17. End Class


    Ich lege eine Instanz der Klasse an, befülle die Eigenschaften mit Inhalt und dann wird gespeichert:

    VB.NET-Quellcode

    1. ' Serialisiert ValfFunction in eine XML-Datei und gibt True zurück, wenn die Datei erfolgreich geschrieben wurde
    2. Friend Function WriteValfFunctionToFile(ByVal strFileName As String, ByVal objValfFunction As Object) As Boolean
    3. Dim blnFileExists As Boolean = False
    4. Dim xmlSerializeValfFunction As XmlSerializer = New XmlSerializer(GetType(ValfFunction))
    5. Try
    6. ' Stream zum Schreiben öffnen
    7. Dim streamFunction As TextWriter = New StreamWriter(strFileName)
    8. ' Serialisierte Daten schreiben
    9. xmlSerializeValfFunction.Serialize(streamFunction, objValfFunction)
    10. ' Stream schließen
    11. streamFunction.Close()
    12. Catch exFile As Exception ' Was immer auch passiert...
    13. Dim strCaption As String = "FPlotFileSystem.WriteValfFunctionToFile"
    14. Dim strMsgText As String = exFile.Message
    15. MessageBox.Show(strMsgText, strCaption)
    16. End Try
    17. blnFileExists = File.Exists(strFileName)
    18. Return blnFileExists
    19. End Function


    Das Ergebnis in XML für die simple Funktion A * x, also mit zwei Symbolen, davon einer Laufvariablen, sieht dann so aus:

    XML-Quellcode

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <ValfFunction xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    3. <ValfTermInFix>A*x</ValfTermInFix>
    4. <SymbolsCount>2</SymbolsCount>
    5. <Symbols>
    6. <OneSymbol>
    7. <SymbolName>x</SymbolName>
    8. <SymbolValue>0</SymbolValue>
    9. <IsControlVar>true</IsControlVar>
    10. </OneSymbol>
    11. <OneSymbol>
    12. <SymbolName>A</SymbolName>
    13. <SymbolValue>4</SymbolValue>
    14. <IsControlVar>false</IsControlVar>
    15. </OneSymbol>
    16. </Symbols>
    17. </ValfFunction>


    Wichtig bei der zu serialisierenden Klasse ist, dass alle Eigenschaften, die in die XML-Datei geschrieben werden sollen, Public sind. Ich habe spaßeshalber mal Public durch Friend ersetzt, schon stand nur noch der XML-Header in der Datei. Dadurch kann man aber auch einfach festlegen, welche Elemente einer Klasse serialisiert werden. Alles, was nicht serialisiert werden soll, eben nicht Public machen.

    Eingelesen werden muss das ja auch noch. das sieht dann so aus:

    VB.NET-Quellcode

    1. Friend Function ReadValfFunctionFromFile(ByVal strFileName As String, ByRef objValfFunction As Object) As Boolean
    2. Dim blnSuccess As Boolean = False
    3. Dim xmlSerializeValfFunction As XmlSerializer = New XmlSerializer(GetType(ValfFunction))
    4. Try
    5. ' Stream zum Lesen öffnen
    6. Dim streamFunction As FileStream = New FileStream(strFileName, FileMode.Open)
    7. objValfFunction = CType(xmlSerializeValfFunction.Deserialize(streamFunction), ValfFunction)
    8. blnSuccess = True ' Bis hierher alles gut gegangen
    9. Catch exFile As Exception
    10. Dim strCaption As String = "FPlotFileSystem.ReadValfFunctionFromFile"
    11. Dim strMsgText As String = exFile.Message
    12. MessageBox.Show(strMsgText, strCaption)
    13. End Try
    14. Return blnSuccess
    15. End Function


    Jetzt werd ich alles serialisieren, was mir in die Finger kommt ... ;)

    Gruß
    Carbonunit

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Carbonunit“ ()

    @ErfinderDesRades: MIt dem Exception-Handling hast du natürlich recht, die IDE kann das viel besser, als ich mit meiner Messagebox.
    Wenn ich wohin schreib, wo ich auch schreiben darf, wenn das zu Lesende auf Anhieb gefunden wird und wenn sich zwischendurch die serialisierte Klasse nicht geändert hat (also wenn ich sie nicht geändert hab), passiert eigentlich auch nichts. Da kann das Try-Catch weg und ich seh dann besser, was wirklich schiefgehen kann.
    Fehlerbehandlung ist auch noch eine Baustelle, da gibt es zur Zeit in meinem Code einen wilden Mix aus Rückgabewerten und Exception-Handling.

    Dein Tut hab ich gelesen, ist interessant und auf jeden Fall hilfreich. Mit Fehlern kann/sollte/muss man auf verschieidene Arten umgehen:
    1. Vermeiden. Klar, das ist die beste Methode, sauber Programmieren, Ojekte, mit denen ich was machen will, sollten nicht Nothing sein. Sind sie das, hab ich zu 99% beim Programmieren vorher Mist gebaut.
    2. Umgehen: Wenn die Datei mit den Programm-Settings nicht gefunden wird, nehm ich eben Default-Werte, die musste ich ja auch nehmen, als es die Datei mit den Programmsettings noch gar nicht gab. Die Defaults waren sicher vor der Datei da, denn mit irgendwas muss man ja anfangen.
    3. Beenden: Wenn irgendwelche wichtigen Daten nicht gefunden werden, auf denen meine Software basiert, kann man damit nichts machen. Also aufschreiben, was los war, dem User nett auf Wiedersehen sagen und den Task beenden.

    Nach der Strategie werde ich das bei mir noch genau durchsehen. Es soll ja auch noch eine Makrosprache rein, die das ganze Ding durch Code steuert, den vielleicht irgendein DAU zusammengeschustert hat (Na gut, meistens werd das schon ich gewesen sein...).
    Deinen weisen Worten folgend, schmeiß ich erstmal alle Try-Catches wieder raus und setz die Stellen als Fehlerbehnadlung auf die ToDo-Liste. Oft weiß ich ja auch gar nicht, wie die Exception wohl heißen wird, die da auftreten könnte. Wenn sie niemals nicht auftritt, muss sie wohl auch nicht behandelt werden.

    Ich denk aber auch, dass bei kommerzieller Software schon der Markt zu (irgend)einer Fehlerbehandlung zwingt. Denk nur dran, wie man sich gefühlt hat, wenn man in Windows vor einer "Allgemeinen Schutzverletzung" oder gar einem "Unrecoverable Application Error" saß. Das haben zahlende Kunden gar nicht gerne. Die wollen dann wenigstens eine nette Messagebox mit einer Telefonnummer, die sie anrufen können, damit der Servicetechniker kommt. Wenn der dann davor sitzt, findet er den Fehler natürlich auch nicht, bastelt noch eine Fehlerbehandlung rein und verschwindet wieder. Da er aber SAbvA beherrscht (Sicheres Auftreten bei völliger Ahnungslosigkeit), denkt der Kunde tatsächlich eine Weile, der hätte ihm echt geholfen.
    Hallo Carbonunit,

    wie ich sehe schreibst du deinen eigenen Formelparser und ich will dir da auch nicht reinpfuschen, aber du könntest dir, um die Möglichkeiten mal anzusehen, diesen Code hier
    activevb.de/rubriken/projekte/prj_3dfp/3dfp.html
    ansehen.

    lg, Vb3-Guru
    @VB3-Guru: 3D ist schon toll, schau ich mir bei gelegenheit mal an.
    @ErfinderDesRades: Was ist denn von diesem ExceptionHandling zu halten?

    VB.NET-Quellcode

    1. Try
    2. ' Stream zum Lesen öffnen
    3. Dim streamFPlotFunctions As FileStream = New FileStream(strFileName, FileMode.Open)
    4. ' Serialisierte Daten lesen
    5. gobjFPlotFunctions = CType(xmlSerializeFPlotFunctions.Deserialize(streamFPlotFunctions), FPlotFunctions)
    6. blnSuccess = True ' Bis hierher alles gut gegangen
    7. Catch exFile As IOException ' Datei in Benutzung
    8. Catch exFile As InvalidOperationException ' XML Korrput
    9. Dim strCaption As String = exFile.Source
    10. Dim strMsgText As String = exFile.Message
    11. MessageBox.Show(strMsgText, strCaption)
    12. Catch exfile As Exception ' was auch immer
    13. Throw exfile
    14. End Try

    Ich kenne zwei Exceptions, die ich durch Rumspielen rausgefunden habe. Machen kann man da nicht viel, aber ich kann dem User anzeigen, warum er seine Datei jetzt nicht laden kann. Kommt dann eine, die ich nicht kenn, werf ich damit in der Gegend rum ;) und die müsste ja dann in der IDE aufschlagen, weil nicht behandelt.
    Sieht mir nicht soo gut aus.
    Ist das gewünscht, dass bei einer IOException die Anwendung weiter läuft, als sei nix passiert? Geht das überhaupt?
    Ebenso weiterlaufen wirds bei einer InvalidOperationException? Nur hier kommt noch die berühmte Messagebox, von der sich TryCatcher ja immer so viel versprechen.

    Den letzten Catch kannste auch weglassen, dann fliegt die Exception ja ebensogut.
    Und das ist auch empfehlenswert, denn wenn du eingreifst, stopt der Code am Eingreifpunkt, wenn du Finger davon lässt, stopt der Code genau da, wo der Fehler auch auftritt - ein wertvoller Vorteil, und kost dich nichts weiter als weniger zu coden.

    Keine Ahnung, ob das so für dich gut ist, da scheint es ja noch einen Success-Boolean zu geben - also im Grunde ist die ganze Fehlerbehandlung hier wohl garnicht zu sehen.
    Generell ist Fehlerbehandlung über Boolean-Rückgabewerte fehlerlastig. Weil eine Methode kann man auch aufrufen, ohne ihre Rückgabe auszuwerten.
    Hingegen um eine Exception kann man nicht versehentlich ignorieren.



    Wie gelegentlich erwähnt, kann man Exceptions eiglich erst behandeln, wenn die Anwendung fertig ist, und man konzipieren kann, wie mit dem User interagieren, wenn eine Exception auftritt.
    Mein veröffentlichter Formelparser zB ist garnicht fertig, es ist ein Experimentier-Dingens. Eine Abwandlung davon hab ich mal wo eingebaut, da war dann im Fehlerfall "<Err>" auszugeben.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    An der Stelle wird eine Projektdatei geladen, das heißt es werden eine oder mehrere Funktionsdateien (auch XML natürlich) in den Funktionsspeicher geladen. Deren Pfade stehen in der hier geladenen Projektdatei.
    Darum auch der eher allgemein gehaltene Rückgabewert blnSuccess. Wenn der False ist, brauch ich nicht weitermachen. Was schiefgegangen ist, erfährt der User durch Messageboxen.

    Die IOException kommt z.B. dann, wenn irgendetwas noch ein Handle auf die Datei offen hat.
    Die InvalidOperationException kommt z.B. dann, wenn das XML-Format irgendwie korrupt ist, ich habs zum Testen mit Notepad ein bisschen verändert.
    Das heißt für den Anwender nach meinem Verständnis: Diese Projektdatei kann er jetzt nicht laden, aber durchaus eine andere, also warum soll die Anwendung nicht weiterlaufen? Ich sollte dem User natürlich mitteilen, dass er seine gewünschten Daten nicht bekommt und warum nicht, deshalb die Messagebox.
    Maßnahmen um das zu beheben sind wohl nur außerhalb meiner Anwendung möglich, also weiß ich auch nicht, was ich sonst machen soll. Klar, während der Entwicklungsphase hilft es mir schon, in der IDE genauer zu sehen, wo, wie und warum es kracht.

    Die Idee hinter diesem Konstrukt ist eigentlich, dass ich bekannte Fehler als Exception abfange und hoffe, dass irgendwann ein neuer Fehler auftritt, den ich dann auch abfangen kann. Mir fallen spontan drei Dinge ein, die schiefgehen können, wenn ich eine Datei öffnen und lesen will:
    1. Datei oder Pfad nicht gefunden. Das fängt schon der FileOpen-Dialog ab, der mir den Dateinamen liefert. Wenn ich da den zusammengeklickten Namen von Hand ändere, merkt der sofort, dass es die Datei nicht gibt und lässt mich nochmal suchen.
    Das müsste ich spätestens dann abfangen, wenn ich wiederverwendbaren Code für andere baue, bei denen ich nicht weiß, wo sie ihren Dateinamen her bekommen.
    2. Datei in Benutzung - Kriege ich mit
    3. Datei korrupt - Kriege ich auch mit
    4. Was noch? Keine Ahnung... Wenn das nie auftritt, werde ich es wohl auch nie erfahren.

    Ich habe mal noch eine FileNotFoundException abgefangen und den Catch und Throw der allgemeinen Exception wieder rausgeworfen. Sollte jemals der noch unbekannte vierte Fall eintreten, zeigt mir die IDE dann ja genau, was los ist.

    Zeichnen muss ich ja auch mal irgendwann...

    Hallo,

    die Sache mit der Serialisierung habe ich verstanden und denke, dass ich es umsetzen kann. Exception-Handling ist dabei sicher noch ein Thema und ich werde die Überlegungen vom @ErfinderDesRades dabei natürlich berücksichtigen.

    Wer einen Funktionsplotter bauen will, der muss irgendwann auch mal Zeichnen. Geplant ist folgendes:
    Der Funktionsplotter kann 16 verschiedene Funktionen speichern und damit arbeiten. Also soll es auch maximal 16 verschiedene Ausgabefenster für die Graphen geben. Es kann natürlich auch sinnvoll sein, dass man zwei Graphen im selben Fenster sehen will zum Vergleichen oder warum auch immer. Also kann jedes der maximal 16 Fenster eine oder mehrere Funktionen darstellen, jede der maximal 16 Funktionen kann aber nur in einem Fenster gezeichnet werden. Das Zeichenfenster kennt auch das Koordinatenkreuz und damit die Skalierung der Zeichnung.

    Jetzt habe ich hier (GDI+ permanent Zeichnen während der Laufzeit) gelernt, dass .Net dem Zeichner es nicht ganz so leicht macht, wie noch VB6.0. Da habe ich auf meine Picturebox gemalt und die hat sich darum gekümmert, dass das Bild erhalten geblieben ist.
    Dasselbe habe ich in .NET schon mal mit einem Koordinatenkreuz probiert. Das sehe ich nur beim Debuggen, wenn ich unmittelbar nach den Zeichenbefehlen auf das Fenster wechsel, dann ist da mein Kreuz zu sehen, danach ist es wieder weg.

    Jetzt muss ich im Paint-Event zeichnen und das immer wieder, denn das wird dauernd aufgerufen. Da sehe ich doch arge Performanceproblem auf alle beteiligten Rechenknechte zukommen. So ein Funktionsgraph wird schon mal ein paar hundert, wenn nicht tausende Punkte haben, die mit einer Linie zu verbinden sind. Wenn dann pro Fenster fünf oder sechs Graphen gezeigt werden sollen, ist der Rechner ganz schnell gut ausgelastet. Natürlich werde ich meinen Term nicht für jede Zeichnung neu berechnen, die Ergebnisse kann ich in einer Tabelle ablegen. Aber die kostet Speicher und jeder Graph braucht wieder eine Tabelle. Auch wenn der fertige Graph einfach nur angezeigt wird, kostet er ständig Rechenleistung, denn spätestens nach jedem Öffnen eine Menues, welches das Bild teilweise überdeckt, muss ich neu zeichnen. Ich und nicht das Betriebssystem, so wie früher... Schnief, damals war doch alles besser... und aus Holz... ;)

    Eine andere Möglichkeit zeigt dieser Post auf: Linie auf Picturbox zeichnen klappt nicht so recht
    Die BackgroundImage-Eigenschaft der Picturebox. Die kann ich aber wohl nur aus einer Datei befüllen. Also müsste ich zunächst in eine Datei zeichnen und diese dann in mein Fenster laden. Auch nicht besonders performant, denn nicht jeder hat eine SSD-Platte, aber wenigstens bleibt das Bild erhalten und was als Datei gespeichert wurde, muss man nicht im Speicher behalten.

    Soweit meine, sicher noch unausgegorenen, Gedanken zum Thema. Sicher habt ihr da Erfahrungen. Ist es tatsächlich so "teuer", wie ich vermute, alles im Paint-Event immer wieder zu zeichen? Ist das mit dem BackgroundImage die bessere Idee? Oder gibt es noch irgendetwas, das ich gar nicht gesehen habe bisher?

    Gruß
    Carbonunit

    Gruß
    Carbonunit
    zu post#8: ok, das versteh ich nun so, dass deine Anwendung soweit auskonzipiert ist, dass klar ist, was der User tun kann, wenn einer der in Catches vorgesehenen Fehler auftritt (anneres ProjektFile versuchen).

    Und es ist nicht vorgesehen, deinen FormelParser irgendwo anders zu verwenden, wo die Dinge vlt. anders liegen können.

    zu post#9: nein - es ist ühaupt nicht teuer, im Paint-Event zu zeichnen.
    Sondern das ist derselbe Prozess (derselbe, nicht nur der gleiche), mit dem jedes Control sich selber zeichnet.
    Man musses allerdings richtig anfassen, und darf keine Performance-Bremsen oder Memory/Resource-Leaks in die ZeichenRoutine einbauen.

    Du könntest auch überlegen, das MS-ChartControl zu nutzen - ein gutes Chart selbst zu zeichnen ist nämlich eine irrwitzig komplizierte Aufgabe - man muss sich um jedes Strichlein, jeden Text, selber kümmern, und Resizen berücksichtigen, evtl. eine Scrollbar, evtl. auch die Mausposition und und und....

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()