Wie geht ihr mit abfangbaren Problemen um? Oldschool vs gutes Design?

  • VB.NET

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von John422.

    Wie geht ihr mit abfangbaren Problemen um? Oldschool vs gutes Design?

    Hi,

    ich kommen ursprünglich von Urlaltem Basic, dann VB6, lange Pause und jetzt seit einer Weile bei VB.Net, WinForms. Bisher kein WPF.

    Was ich programmiere, läuft zwar (und das gut) aber ich bin mit meinen Stil nicht zufrieden und werde den Eindruckl nicht los, dass er deutlich oldschool ist und ich mir damit zu viel Arbeit mache.

    Beispiel:
    Anwendung, die sich Daten per SQL holt.
    Ich habe eigene Klassen, erzeuge Instanzen, befülle sie aus Daten der SQL Abfrage.
    Dabei kann es zu erkennbaren Problemen kommen, z.B. liefert eine Abfrage keine Daten. Das ist als Ergebnis OK aber der Programmablauf kann dann nicht weiter gehen.

    Bei mir hat typischerweise die Klasse eine Funktion

    VB.NET-Quellcode

    1. HoleDaten(byRef lstDaten as List Of(clsMeineDatenKlasse) As Boolean


    Geht was schief, wird FALSE zurück gegeben. Oldschool eben.

    Wie kann man so etwas besser machen?
    Ein Public Event Fehler(), das man auslöst?
    Oder ein Throw Exeption?

    Mir fehlt da wirklich etwas Schubs in die richtige Richtung..

    Vielen Dank,

    John
    Würde die Liste returnen. Im Fehlerfall evtl Null oder instanziert ohne inhalt. Dann prüfen in welchem Zustand die Liste ist und entsprechend reagieren. Hab mir für solch einen Fall eine IsNullOrEmpty Extension geschrieben.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Es kommt drauf an.
    Bei programminternen Problemen habe ich pro Form eine eigene Datei, in der ich Datenüberprüfungsfunktionen habe, in denen Datenpakete validiert werden. Werden Fehler in z.B. eingegebenen Daten gefunden, kommt ne MessageBox, die den Fehler zeigt und die Verarbeitung abbricht, sodass der User nachbessern kann:

    VB.NET-Quellcode

    1. Private Sub CreateProcess(ProcessData As ProcessData)
    2. If ComplainingAboutInvalidData(ProcessData) Then Return
    3. CreateProcessFromValidData(ProcessData)
    4. End Sub
    5. Private CreateProcessFromValidData(ProcessData As ProcessData)
    6. '…
    7. End Sub
    8. 'in eigener Partial File
    9. Private Function ComplainingAboutInvalidData(ProcessData As ProcessData) As Boolean
    10. If ProcessData.ProcessDate > Date.Today Then MessageBox.Show("Das Datum darf nicht in der Zukunft liegen."): Return True
    11. If Not ProcessData.Items.Any Then MessageBox.Show("Die zu bestellenden Artikel fehlen."): Return True
    12. If ProcessData.Items(Function(x) x.PackCount = 0) Then MessageBox.Show("Bei allen zu bestellenden Artikel muss die Packungsanzahl größer als Null sein."): Return True
    13. Return False
    14. End Function

    Ich habe eine DLL, die mir Daten aus meiner Datenquelle holt. Da verwende ich keine MessageBox, sondern feuere ein Event und die eigentliche App reagiert darauf. Dann sag ich z.B. »keine Verbindung mit der Datenquelle vorhanden, das Programm wird beendet«. Eben weil viele meiner Programme ohne extern gespeicherte Daten nicht laufen. Und es ergäbe auch keinen Sinn, dass das Programm dann weiterläuft, da neu erzeugte Daten nicht in die Datenquelle zurückgeschrieben werden können, wenn schon von Anfang an klar ist, dass die Datenquelle nicht erreichbar ist.

    ExceptionThrowing taucht bei mir nur dann auf, wenn ich nicht damit rechne, dass das Programm bis zu jener Codezeile überhaupt kommen kann:

    VB.NET-Quellcode

    1. Private Function GetDescriptionFrom(ProcessState As ProcessState) As String
    2. Select Case ProcessState 'alle eingebauten Statusangaben sind aufgelistet
    3. Case ProcessState.NotSentYet: Return "noch nicht übermittelt"
    4. Case ProcessState.Sent: Return "übermittelt, aber noch nicht geliefert"
    5. Case ProcessState.Delivered: Return "geliefert"
    6. End Select
    7. Throw New ArgumentException($"{ProcessState} kam unerwartet.")
    8. End Function
    9. End Sub

    Abfangen von Exceptions entsprechend dem Thread Fange nur Exceptions ab, die Du kennst und sinnvoll bearbeiten kannst.
    Unerwartete Fehler fange ich mit einer programmweiten Routine ab, erfasse die greifbaren Daten und mache automatisch einen Screenshot. Außerdem informiere ich den User, dass was schiefgelaufen ist und ermögliche es ihm, die Daten zu speichern und das Programm zu beenden.

    btw: Auch wenn es nur ein Beispiel von Dir war: das ByRef in Deiner Codezeile musst Du nur dann verwenden, wenn irgendwo in der Methode die Zeile lstDaten = auftaucht.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    das ByRef
    ich hau in dieselbe Kerbe: ByRef ist ein Spezial-Konstrukt für Fälle, wo eine Methode mehrere Werte verschiedenen Typs returnen muss.
    Das ist wirklich sehr speziell, und hier schoma garnet der Fall. Am besten, du streichst erstmal ByRef aus deinem Wortschatz.
    (Dassis schoma irritierend, weil vb6->vb.net - Konvertierer übergeben alle Vars byRef, weil das in vb6 halt Voreinstellung war.
    Aber es ist ein gewisses Risiko der Code-Sicherheit, und in .Net braucht mans nicht, und dann soll mans auch lassen.)
    Nochmal mein Einwurf: Sollte es wirklich so sein, dass in der Methode steht

    VB.NET-Quellcode

    1. Funtion HoleDaten(ByRef lstDaten As List(Of clsMeineDatenKlasse) As Boolean
    2. '…
    3. lstDaten = New List(Of clsMeineDatenKlasse)
    4. '…
    5. End Function
    dann muss der Parameter mit ByRef versehen werden. Ansonsten soll der ByRef-Zusatz weg. Eine von außen hereingereichte Klasseninstanz wird auch ohne ByRef in vollem Umfang innerhalb der Methode nutzbar sein.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Moin moin

    ​HoleDaten(byRef lstDaten as List Of(clsMeineDatenKlasse) As Boolean ( Old school eben )

    Was ist daran falsch?
    Wie man dann mit dem False weiter umgeht, das kann man doch individuell machen.

    Ich habe mir für so etwas eine "LoggerClass" gemacht, welche die Fehler in ein "LogFile einträgt und dann ggf eine Nachricht im Label anzeigt oder auch mal eine MsgBox wirft.

    Ebenso verwende ich dann manchmal das Try / Catch das dann auch in die "LoggerClass" führt.
    Asperger Autistin. Brauche immer etwas um gewisse Sachen zu verstehen. :huh:
    Es kommt auf die verwendete Sprache und den Fehler an; ist also immer eine Einzelfallentscheidung.

    Deine Variante ähnelt halt stark dem üblichen Vorgehen in C und (prä C++11) C++:

    C-Quellcode

    1. int foo(void* item, const int itemType);


    In modernen Hochsprachen gibt's jedoch viele unterschiedliche Arten und Weisen, mit Fehlern umzugehen.
    Im Sinne von Clean-Code ist es übrigens nicht, eine Funktion nur ein bool zurückgeben zu lassen, wenn es mehrere Fehlerfälle gibt.
    So gibst du nur zurück, dass etwas schiefgelaufen ist. Mit einem int kannste zumindest sagen, was genau schiefgelaufen ist.
    Bei atomaren Operationen (oder solchen, die nur eine Aufgabe erledigen), kannst du sowas wie TryFoo() implementieren. Dann wäre ein bool auch okay.


    Je nachdem, wie schwerwiegend der Fehler ist, kann man mit Exceptions arbeiten, oder wie hier ebenfalls gesagt wurde mit leeren Referenzen.
    Da muss man aber aufpassen, dass man diese im Programm abfängt. Erwartet dein Programm immer, dass die Referenz zur Liste existiert, kannst du eine leere Liste statt null zurückgeben.

    Andererseits wäre es in diesem Fall durchaus in Ordnung, sowas wie eine DataFetchException zu werfen, um deinem Programm zu signalisieren, dass eine Kernfunktion schiefgelaufen ist.
    Zu Debugging-Zwecken kannst du, wie @Amelie das bereits gesagt hat, mit einem Logger arbeiten. Ich selber würde das zwar nicht selbst implementieren, sondern auf Serilog oder Nlog setzen, aber ist auch ein gangbarer Weg.
    Dort kannst du dann in Debug-Builds auch den StackTrace mit ausgeben lassen und genau sehen, wo es geknallt hat. Wenn eine vorherige Exception der Grund für den Fehler ist, kannst du diese auch als InnerException weiter durchs
    Programm propagieren.

    Was man noch erwähnen sollte, ist dass in Dotnet immer alles außer ValueTypes per Referenz übergeben wird; d.h. deine Liste kann durch die Methode verändert werden und diese Änderung ist auch in dem Aufrufer sichtbar.

    Anbei noch ein paar Beispiele.

    C#-Quellcode

    1. bool GetData(List<int> myList) {
    2. // Fetch data
    3. if (fetchFailed) { return false; }
    4. myList.Add(data);
    5. return true;
    6. }
    7. int GetData(List<int> myList) {
    8. // Fetch data
    9. if (fetchFailed) { return 1; }
    10. if (missingData) { return 2; }
    11. myList.Add(data);
    12. return 0;
    13. }
    14. int GetData(List<int> myList) {
    15. try {
    16. myList.Add(DoFetch());
    17. } catch (Exception ex) {
    18. throw new DataFetchException("Failed to retrieve data from remote", ex);
    19. }
    20. }
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.
    Hallo,
    eine Möglichkeit wäre die Verwendung eines Union Types. Um genauer zu sein den Option-Typ. Die Idee hinter dem Option-Typ ist, dass ein Wert repräsentiert werden kann, der möglicherweise oder möglicherweise nicht existiert. Aktuell bedarf es dazu noch ein Nuget-Paket (NuGet Gallery | LanguageExt.Core 4.4.9), aber es gibt einen Vorschlag für C#, dies in dotnet direkt zu implementieren.

    Deinen Fall könntest du so abdecken (Beispielhaft)

    VB.NET-Quellcode

    1. Module Program
    2. Sub Main(args As String())
    3. Dim r = Foo() ' Funktion zum Daten holen
    4. ' Das Ergebnis "matchen", was genau zurückgegeben wurde
    5. ' Beide Fälle müssen hier abgefangen werden
    6. r.Match(Sub(dataClasses)
    7. 'Some() Daten empfangen
    8. End Sub,
    9. Sub()
    10. ' None() Keine Daten vorhanden
    11. End Sub)
    12. ' Man kann auch einfach nur einen Spezialfall verarbeiten. Dazu dienen
    13. ' IfSome() und IfNone()
    14. r.IfSome(Sub(f)
    15. ' Happy Part
    16. End Sub)
    17. r.IfNone(Sub()
    18. ' Sad Part
    19. End Sub)
    20. End Sub
    21. Function Foo() As [Option](Of List(Of MyDataClass))
    22. Dim l = New List(Of MyDataClass) From {New MyDataClass() With {.V = "Demo1", .W = 1}, New MyDataClass() With {.V = "Demo2", .W = 1}}
    23. 'Return Nothing
    24. Return l
    25. End Function
    26. Class MyDataClass
    27. Public V As String
    28. Public W As Integer
    29. End Class
    30. End Module


    Sollte in deiner Funktion aber eine Exception geworfen werden, kann man auch mit dem Result-Typ arbeiten. Bei Result kann man die Exception ebenfalls als Rückgabe ausgeben und im weiteren Programmablauf mit IfSucc() und IfFail() des Result-Typs arbeiten.

    VB.NET-Quellcode

    1. Function Bar() As Result(Of List(Of MyDataClass))
    2. Dim l = New List(Of MyDataClass) From {New MyDataClass() With {.V = "Demo1", .W = 1}, New MyDataClass() With {.V = "Demo2", .W = 1}}
    3. If True Then
    4. Return New Result(Of List(Of MyDataClass))(New Exception("ARG"))
    5. End If
    6. Return l
    7. End Function


    Prinzipiell kann man das auch mit Null-Typen erreichen, aber die Verwendung eines Option-Typ kann dazu beitragen, Code leichter zu verstehen.

    Amelie schrieb:

    Was ist daran falsch?
    Wie man dann mit dem False weiter umgeht, das kann man doch individuell machen.
    Ganz einfach: Was bedeutet denn True oder False bei einer Function, deren Name HoleDaten aber eine Anweisung ist? Allein der Anfang ist falsch. Die Methode kann ja HoleDaten heißen (obwohl die Benennung sehr allgemein gehalten ist; was für Daten, aus welcher Quelle, …). Aber das ist der Name für eine Sub, nicht für eine Function. Eine Function soll den Zustand von etwas wiedergeben, aber soll keine Daten ändern, eine Sub soll was machen, aber keine Zustände (z.B. per ByRef-Parameter) wiedergeben, siehe CQS. Aber eine Methode, die etwas macht und einen Wert wiedergibt (und dann noch einen mit hierfür ziemlich mieser Aussagekraft) ist schwerer zu durchschauen. Denn wie gefragt: Was bedeutet denn True oder False als Ergebnis für die Methode HoleDaten? Falsche Parameter übergeben? Keine Datenquelle verfügbar? Keine Daten verfügbar? Das Programm hat keine Lust, Daten zu holen (Stichwort KI)?

    Folgender Aufbau wäre zielführender:

    VB.NET-Quellcode

    1. Sub HoleDatenUndPackeSieIn(MeineListe As List(Of MeinObjekt))
    2. Melde, wenn übergebende Parameter falsch sind und verlasse die Methode
    3. Melde, wenn keine Datenquelle vorhanden sind und verlasse die Methode
    4. Melde, wenn keine Daten vorhanden sind und verlasse die Methode
    5. Baue die Verbindung zur Datenquelle auf
    6. Merke Dir die Daten aus der Datenquelle
    7. Packe die vorhandenen Daten aus der erreichbaren Datenquelle in die existierende Liste namens MeineListe
    8. End Sub

    So kann man dem Programm (per Exception) oder dem User (per Fehleranzeige) detailliert klarmachen, ob der Vorgang funktioniert hat und wenn nicht, woran es lag. Aber ein einfaches True/False ist m.E. irgendwie … nix.

    Der Aufbau hat einen von zwei Nachteilen:
    • Entweder muss man 2 Abfragen pro Vorgang machen, also z.B. 1. gibt es Daten (Zeile#4) und 2. merke Dir die Daten (Z#8).
    • Oder man ruft einen Zustand ab, merkt sich den Wert und macht damit die Validierung. Dann ist das Ganze immer abwechselnd Abfrage und Validierung. Find ich nicht so übersichtlich, daher teile ich solch eine Methode noch auf, damit Datenvalidierungen für die Übersicht immer nur am Anfang der Methode stehen.
    Aber eben auch die genannten Vorteile, die m.E. überwiegen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

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

    Vollzitat eines Vorposts an dieser Stelle entfernt ~VaporiZed

    Die Liste lstDaten dient in diesem Fall zur Rückgabe der Daten, die durch die Methode HoleDaten() abgerufen wurden.
    Sprich, ich muß die Liste leeren und mit neuen Objekten befüllen können und das Ergebnis soll in der aufrufenden Klasse bereitgestellt werden.
    Ohne ByRef entspricht das doch eine ByVal, womit auch zwar die Liste in meiner Methode manipulieren könnte, das Ergebnis jedoch nicht zurück erhalten würde.

    Oder sehe ich das falsch?

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

    Ein Wort sagt mehr als tausend Bilder.
    Bilder
    • Result.png

      38,71 kB, 651×255, 18 mal angesehen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Hier ist das nochmal nachzulesen.

    learn.microsoft.com/de-de/dotn…by-value-and-by-reference

    Finde ich aber nicht schön gemacht.
    Ich erwarte eigentlich identisches Verhalten, egal ob ich jetzt einen String oder einen List Of (String) übergebe.

    Die Prozedur kann nicht die Variable, aber Member der Instanz ändern, auf die sie verweist.


    Was für einen Sinn macht das?
    Ich kann also nicht machen:

    VB.NET-Quellcode

    1. lstDaten = New List(Of clsMeineDatenKlasse)


    aber

    VB.NET-Quellcode

    1. lstDaten.Clear


    Wo ist der Unterschied im Ergebnis?

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

    naja, guck deine Codes doch an.
    Im ersten Fall ist in lstDaten ein anderes Objekt. Das Objekt, was vorher da war, existiert zwar noch, aber die Variable lstDaten enthält und ein anderes Objekt - nicht mehr das, wasses vorher war.

    im zweiten Fall ist lstDaten nachwievor dasselbe, nur eben geleert.

    Kannst du da keinen Unterschied erkennen?
    Ja, verstehe ich. Aber das ist doch dem Aufrufenden egal, solange da nachher die Daten in Form einer Liste zurückkommen und das kommen sie doch, egal ob ich nun .Clear oder = New mache und danach befülle.

    Zurück zu ByRef.
    ich finde das unzureichend abstrahiert.
    Als Nutzer einer Hochsprache möchte ich in meiner Fnktion oder Methode festlegen, wie die Daten rein- und wieder rauskommen können und da erwarte ich
    ByVal = unanhängige Kopie
    ByRef = die Daten selbst
    Genauso ist es z.B. für eine String umgesezt. Wieso nicht auch für eine ListOf (Char) die nichts anderes als eine String ist?

    Technisch mag es begründbar sein, weil eben nur ein Pointer auf die Datenstruktur übergeben wird.
    Aber wieso muß ich hier differenzieren?
    Ist es nicht Aufgabe der Hochsprache, dass ich mich gerade icht darum kümmern muß, wie im Hintergrund Daten abgebildet werden?

    Ich find das inkonsequent.

    John422 schrieb:

    Aber wieso muß ich hier differenzieren?
    Die New Anweisung sagt: Schreib xy in den Arbeitsspeicher. Dafür muss ein freier Platz verwendet werden. Wenn das Objekt geschrieben steht, kommt die Zuweisung, da wird n Zeiger für das neue Objekt gebaut.
    Und das alte Objekt muss aufgeräumt werden.
    Bei jeder Argumentübergabe an eine Methode dieses Spielchen automatisch mit machen, findest du? Ich denke da gibt es viel Widerspruch und deswegen gibt es das nicht.
    Manche Argumente werden von Funktion zu Funktion gereicht. Manchmal nur durchgereicht.
    Eine Kopie zu erzeugen ist auch nicht sonderlich anspruchsvoll. Gerade bei Sammlungen reicht ein .ToList oder .ToArray und schon übergibt man eine Kopie. Es ist halt nur nicht per Schlüsselwort, sondern als explizite Funktion.

    Und ByRef ist abstrakter als es aussieht. Es sind eben nicht nur die Daten selbst, es ist der Zeiger auf diese Daten, der übergeben wird. Wenn ein Referenztyp mit ByVal übergeben wird, werden auch die Daten selbst übergeben, aber ein neuer Zeiger erstellt. Siehe diesen Unterschied auch in Post 5 nochmal dargestellt. Mit ByVal kriegst du von der neuen Liste nix mit.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    John422 schrieb:

    Aber das ist doch dem Aufrufenden egal, solange da nachher die Daten in Form einer Liste zurückkommen und das kommen sie doch, egal ob ich nun .Clear oder = New mache und danach befülle.
    Wenn Du eine fremde Methode hast, die die Klasseninstanz per ByRef haben will, besteht immer der Verdacht, dass die Methode die vorhandene Instanz überschreibt. Das kann akzeptabel sein. Oder auch nicht. Das kommt auf die Klasse drauf an. Das ist wie bei einer klassenweiten Variable, die man per New deklariert und definiert. Von Haus aus macht VS den Vorschlag: »Mach es ReadOnly, damit niemand versehentlich die zum Start erstellte Instanz überschreiben kann.«:



    John422 schrieb:

    da erwarte ich
    ByVal = unanhängige Kopie
    Da Klasseninstanzen sehr groß sein können, würde die dauerhafte eine Kopie eines 100 MB-Monsters ggf. schnell mal zu einem Speicherproblem werden können. Und es würde sicherlich auch der Performance erheblich schaden. Das war m.E. ein Grund, warum Klasseninstanzen als Referenz/Zeiger übergeben werden. Es können eventuell auch Probleme auftreten, wenn es mehrere Objekte gibt, die automatisch erstellt werden und inhaltlich identisch sind, aber mir fällt grad kein Beispiel ein.

    John422 schrieb:

    Wieso nicht auch für eine ListOf (Char) die nichts anderes als eine String ist?
    Frage mit falschen Voraussetzungen. Einer List(Of String) kannst Du nicht kommen mit: .Substring, StartsWith, Contains und all den anderen String-Methoden. Es ist einfach nicht das gleiche, noch nicht mal annähernd. Wenn Du es schaffst, eine Klasse zu schreiben, die von List(Of Char) erbt und sich exakt verhält wie ein String, dann poste das bitte im Sourcecode-Austausch. Ich erwarte ein brauchbares Ergebnis frühestens in einem Jahr.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @VaporiZed

    Ich habe mal dein Code-Fragment aus #3 aufgegriffen und in meine "ApplicationLogger-Class" mit eingebunden. :)
    Funktioniert nach ersten Tests super.

    Wäre das so OK?
    Was könnte ich ggf verbessern?



    Spoiler anzeigen

    VB.NET-Quellcode

    1. '/ file: ErrorHandling.vb
    2. Module ErrorHandling
    3. ' Methode zur Behandlung von Ausnahmen und Protokollierung
    4. Public Sub HandleException(status As ExceptionStatus, Optional ex As Exception = Nothing)
    5. ' Beschreibung basierend auf dem Status abrufen
    6. Dim description As String = GetDescriptionFrom(status)
    7. Dim message As String
    8. If ex IsNot Nothing Then
    9. ' Bei Fehlerstatus die Ausnahmebehandlung durchführen
    10. message = $"{description} {ex.Message}"
    11. ' Fehlermeldung ausgeben
    12. Debug.WriteLine(message)
    13. ' Fehlermeldung in die Logdatei schreiben
    14. ApplicationLogger.ExceptionToFile(message, status)
    15. ' Fehlermeldung an den Benutzer anzeigen (wenn es sich um eine Ausnahme handelt)
    16. If status <> ExceptionStatus.Success Then
    17. MessageBox.Show(message, "Achtung Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Error)
    18. End If
    19. Else
    20. ' Bei Erfolgsstatus oder anderen Status, die keine Ausnahme haben
    21. message = description
    22. Debug.WriteLine(message)
    23. ApplicationLogger.ExceptionToFile(message, status)
    24. If status = ExceptionStatus.Success Then
    25. MessageBox.Show(message, "Erfolg", MessageBoxButtons.OK, MessageBoxIcon.Information)
    26. End If
    27. End If
    28. End Sub
    29. ' Methode zum Abrufen der Beschreibung basierend auf dem Status
    30. Public Function GetDescriptionFrom(status As ExceptionStatus) As String
    31. Select Case status
    32. Case ExceptionStatus.Failure : Return "Ein Fehler trat auf."
    33. Case ExceptionStatus.Success : Return "Die Aktion wurde erfolgreich abgeschlossen."
    34. Case ExceptionStatus.FileNotFound : Return "Die Datei wurde nicht gefunden."
    35. Case ExceptionStatus.IOException : Return "Fehler beim Zugriff auf die Datei / das Laufwerk."
    36. Case ExceptionStatus.GeneralError : Return "Ein unerwarteter Fehler ist aufgetreten."
    37. Case ExceptionStatus.Warning : Return "Warnung. Etwas könnte nicht in Ordnung sein."
    38. Case Else
    39. ' Für unbekannte Status
    40. Return "Unbekannter Fehler aufgetreten."
    41. End Select
    42. End Function
    43. End Module


    Hier angewendet:

    VB.NET-Quellcode

    1. ' File: Settings.vb
    2. <XmlRoot("Settings")>
    3. Public Class SettingsSystems
    4. Private Shared ReadOnly _instance As New Lazy(Of SettingsSystems)(Function() New SettingsSystems())
    5. Public Shared ReadOnly Property Instance As SettingsSystems
    6. Get
    7. Return _instance.Value
    8. End Get
    9. End Property
    10. ' Klasse mit den Dateipfaden etc.
    11. Private ConfigInstance As Configuration = Configuration.Instance
    12. <XmlElement("CurrentFrequency")>
    13. Public Property CurrentFrequency As Integer
    14. <XmlElement("NextMonthFrequency")>
    15. Public Property NextMonthFrequency As Integer
    16. Public Sub SaveSettingsToFile()
    17. Dim filepath As String = ConfigInstance.ConfigXmlFilePath
    18. Dim serializer As New XmlSerializer(GetType(SettingsSystems))
    19. Try
    20. Using writer As New StreamWriter(filepath)
    21. serializer.Serialize(writer, Me)
    22. End Using
    23. ' Erfolgsnachricht an den Benutzer ausgeben und im Log protokollieren
    24. HandleException(ExceptionStatus.Success)
    25. Catch ex As Exception
    26. ' Fehlerbehandlung und Logging
    27. HandleException(ExceptionStatus.Failure, ex)
    28. End Try
    29. End Sub
    30. Public Sub LoadSettingsFromFile()
    31. Dim filepath As String = ConfigInstance.ConfigXmlFilePath
    32. Try
    33. Dim serializer As New XmlSerializer(GetType(SettingsSystems))
    34. Using reader As New StreamReader(filepath)
    35. Dim loadedSettings As SettingsSystems = CType(serializer.Deserialize(reader), SettingsSystems)
    36. Me.CurrentFrequency = loadedSettings.CurrentFrequency
    37. Me.NextMonthFrequency = loadedSettings.NextMonthFrequency
    38. End Using
    39. ' Fehlerbehandlung und Logging
    40. Catch ex As FileNotFoundException
    41. HandleException(ExceptionStatus.FileNotFound, ex)
    42. Catch ex As IOException
    43. HandleException(ExceptionStatus.IOException, ex)
    44. Catch ex As Exception
    45. HandleException(ExceptionStatus.GeneralError, ex)
    46. End Try
    47. End Sub
    48. End Class

    Asperger Autistin. Brauche immer etwas um gewisse Sachen zu verstehen. :huh:
    Hallo,

    John422 schrieb:

    Genauso ist es z.B. für eine String umgesezt.

    Das ist nicht ganz richtig: Auch String ist ein Referenztyp. Allerdings sind strings immutable, also nicht veränderbar. Daher verhält sich string in diesem Fall ähnlich wie ein Werttyp, obwohl es ein Referenztyp ist.

    John422 schrieb:

    Wieso nicht auch für eine ListOf (Char) die nichts anderes als eine String ist?

    String ist keine List(Of Char) sondern String implementiert IEnumerable(Of Char). Jede Liste ist zwar ein IEnumerable, aber nicht jedes IEnumerable auch eine Liste. :)