Zum Inhalt springen

Eigene Klasse in VB.net umschreiben für C#


Eleu

Empfohlene Beiträge

Guten Morgen,

ich habe in VB.net eine einfache Klasse programmiert, die zwei Zahlen addiert.

Public Class Addiere_Variante2
    Function Berechne(ByVal Zahl1 As Integer, Zahl2 As Integer) As Integer
        Return Zahl1 + Zahl2
    End Function
End Class

In der Form, gibt es dann einen Button, mit dem man der Klasse zwei Werte übergibt und der Rückgabewert, wird in eine TextBox geschrieben.

 

 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button4.Click
        Dim clg As New Addiere_Variante2
        TextBox1.Text = clg.Berechne(3, 5)
    End Sub

Mich würde interessieren, wie die Syntax aussehen würde, wenn ich das gleich mit C# mache.

Wäre klasse, wenn mir da jemand helfen könnte.

Gruß

Eleu  

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 11 Minuten schrieb Eleu:

In der Form, gibt es dann einen Button, mit dem man der Klasse zwei Werte übergibt und der Rückgabewert, wird in eine TextBox geschrieben.

Ich verstehe nicht, warum du hier keine statische Klasse (bzw. in VB "Module") verwendest. Für die Methode "Berechne" benötigst du keine Instanz der Klasse (natürlich abhängig davon, was die Klasse noch so kann).

vor 12 Minuten schrieb Eleu:

Mich würde interessieren, wie die Syntax aussehen würde, wenn ich das gleich mit C# mache.

Gewöhne dir an, Dokumentationen zu lesen. Antwort siehe hier: https://docs.microsoft.com/de-de/dotnet/csharp/programming-guide/classes-and-structs/classes

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Stunden schrieb pr0gg3r:

Ich verstehe nicht, warum du hier keine statische Klasse (bzw. in VB "Module") verwendest. Für die Methode "Berechne" benötigst du keine Instanz der Klasse (natürlich abhängig davon, was die Klasse noch so kann).

Gewöhne dir an, Dokumentationen zu lesen. Antwort siehe hier: https://docs.microsoft.com/de-de/dotnet/csharp/programming-guide/classes-and-structs/classes

Kann man machen, würde ich aber nicht tun. Methoden, die statische Methoden aufrufen, sind schwerer zu testen, weil man die statische Methode mittestet. Besonders wenn die statische Methode Fehler verursachen kann, wird es sehr problematisch, da die Lokalisierung des Fehlers aufwendiger wird, da der Fehler ggf. in der statischen Methode aufgetreten ist. Beispiel: Man möchte, dass die Berechne()-Funktion nicht nur das Ergebnis zurückliefern soll, sondern das Ergebnis soll in eine Log-Datei geschrieben werden und beim Schreiben in die Log-Datei könnten Probleme auftreten.

Außerdem würde so eine Klasse gegen das Single-Responsibility-Prinzip verstoßen, weil sie zwei Dinge macht:

  • Zwei Zahlen addieren
  • Ergebnis loggen

Ich persönlich meide statische Funktionen/Methoden so weit, wie es geht. So umgeht man viele Probleme. Ich hab angefangen Klassen so zu schreiben, sodass sie von Außen kaum gesteuert werden. Die Additionsklasse hätte ich wie folgt geschrieben:

Public Class Addition
    private ReadOnly left As Double
    private ReadOnly right As Double

    Public Sub New (left As Double, right As Double)
        Me.left = left
        Me.right = right
    End sub
  
    Public Function Calculate() As Double
        Return Me.left + Me.Right
    End Function
End Class

Das hat jetzt den Vorteil, dass man die Calculate()-Funktion in ein separates Interface ablegen kann, was jede erdenkliche Art von Berechnungen implementieren könnte, wenn wir weiterdenken, als die vier Grundrechenarten. z.B. Quadratwurzel, Sinus, Cosinus, Tangens, etc. Darüber hinaus könnte man noch die Variablen left und right in eine abstrakte Klasse abstrahieren, weil sie auch für die anderen drei Grundrechenarten benötigt werden und wir somit das DRY-Prinzip einhalten. Das ganze Konstrukt könnte dann so aussehen:

Public MustInherit Class ICalculation
    MustOverride Function Calculate() As Double
End Class
Public MustInherit Class BinaryCalculation
    Inherits ICalculation

    Protected ReadOnly left As Double
    Protected ReadOnly right As Double

    public Sub New (left As Double, right As Double)
        Me.left = left
        Me.right = right
    End Sub
End Class
Public Class Addition
    Inherits BinaryCalculation

    Public Sub New(left As Double, right As Double)
        MyBase.New(left, right)
    End Sub

    Public Overrides Function Calculate() As Double
        Return Me.left + Me.right
    End Function
End Class

Die Klassen für Subtraktion, Multiplikation und Division würden analog so aussehen, wie die Addition-Klasse. Eine Klasse für einen Logger könnte so aussehen:

Public Class LoggableCalculation
    Inherits ICalculation
    
    Private ReadOnly calculation As ICalculation
    private ReadOnly logger As Logger

    Public Sub New(calculation As ICalculation, logger As Logger)
        Me.calculation = calculation
        Me.Logger = logger
    End Sub

    Public Overrides Function Calculate() As Double
        Dim result as Double
        result = Me.calculation.Calculate()
        Me.Log(result)
        Return result
    End Function
  
    private Sub Log(result As Double)
        If(Not Me.logger Is Nothing)
            logger.Log(result)
        End If
    End Sub
End Class

Es ist dem Logger nun egal, ob es eine Addition oder die Berechnung zur Weltherrschaft ist, solange calculation das ICalculation-Interface implementiert. Somit könnte man die Berechnungen und den Logger isoliert testen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Man könnte ja auch mehrere Operationen direkt in der Klasse programmieren und die Auswahl als Parameter übergeben.

Public Class Calculator
    Private ReadOnly left As Double
    Private ReadOnly right As Double
    Private ReadOnly operat As Double
    Public Sub New(left As Double, right As Double, operat As Double)
        Me.left = left
        Me.right = right
        Me.operat = operat
    End Sub
    Public Function Calculate() As Double
        If Me.operat = 0 Then
            Return Me.left + Me.right
        ElseIf Me.operat = 1 Then
            Return Me.left * Me.right
        ElseIf Me.operat = 2 Then
            Return Me.left / Me.right
        End If
        Return 0
    End Function
End Class

und im Button dann für die Multiplikation dann:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim gag As New Calculator(2, 5, 1)
        TextBox1.Text = gag.Calculate
End Sub

Aber das verstößt dann auch gegen das Single-Responsibility-Prinzip, weil sie dann drei Dinge macht? 

 

Bearbeitet von Eleu
Link zu diesem Kommentar
Auf anderen Seiten teilen

Genau, die Methode macht einfach zu viel. Außerdem ist es sie schwieriger zu testen, da die Funktion nun eine sog. zyklomatische Komplexität von 5 hat. Das bedeutet, dass deine Funktion fünf Pfade hat, die durchlaufen werden können. Wenn du jetzt Unittests schreiben würdest, müsstest du mindestens 5 Tests schreiben, nur um diese eine Funktion zu testen. Du merkst auch vielleicht selber, dass du mit Return 0 ein Hilfskonstrukt bauen musstest, damit die Funktion funktionieren kann. Also zusätzliche Komplexität, die beim Lesen nur stört. Desweiteren verstößt diese Art von Porgrammierung gegen das sog. OpenClosed-Prinzip, da wir bei weiterer Funktionalität die Funktion modifizieren müssen.  Angenommen, wir wollen auch noch Modulo-Operator implementieren. Dafür müssen wir dann die Funktion modifizieren. In meiner Variante muss nur eine neue Klasse angelegt werden. Das verhindert, dass der Entwickler, bei der Modifikation, Fehler einbaut, sodass andere Operationen plötzlich nicht mehr funktionieren. Da wir den Code nämlich nicht modifizieren, funktioniert alles so wie gehabt.

Darüberhinaus ist der Funktionsaufruf nun auch noch schwerer zu lesen. Du verstehst vielleicht noch, was die 1 bedeutet aber jemand anderes weiß es nicht und ist entweder auf deine Dokumentation oder auf den Code angewiesen, um nachzuschauen. Sicherlich, die 1 könnte man auch noch gegen ein Enum austauschen, um es sprechender zu machen aber nicht desto trotz hast du die o.g. Probleme. Du musst nämlich eines bedenken: Ein Entwickler liest 80% seiner Tageszeit Code und in den restlichen 20% schreibt er Code und da sollte der Code so verständlich und einfach wie möglich sein.

Das ist zwar schon alles auf einen höheren Niveau, was ich hier erzähle aber ich weiß noch, wie ich angefangen habe, zu entwickeln und für mich wären diese Informationen damals echt Goldwert gewesen. Wenn du es am Anfang nicht verstehst, ist es auch in Ordnung. Softwareentwicklung ist nun mal eine Gratwanderung und in jeder Situation muss man neu abschätzen, welcher Weg am sinnvollsten ist. Es kommt sogar sehr häufig vor, dass man sich verzettelt und sein Plan über den Haufen schmeißen muss, weil eine Situation kommt, an die man nicht gedacht hat.

 

btw. die ganzen Prinzipien, die ich genannt habe, sind unter dem Begriff SOLID-Prinzipien zusammengefasst. SOLID steht für:

  • Single-Responsibility-Prinzip
  • Open-Closed-Prinzip
  • Liskovsches Substitutionsprinzip
  • Interface-Segregation-Prinzip
  • Dependency-Inversion-Prinzip
Bearbeitet von Whiz-zarD
Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo Whiz-zarD,

erst mal vielen Dank für die wertvollen Tipps.

Ich glaube ich habe das soweit verstanden. Die Klasse LoggableCalculation erbt die Funktionen von der Klasse ICalculation. Die Klasse BinaryCalculation, erbt ebenfalls von der Klasse ICalculation. Darüber erfolgt die Trennung und die Funktion kann sich zur Laufzeit ändern

BinaryCalculation, dient dann sozusagen als Schnittstelle zu den Klassen Addition und Multiplikation. Ich habe das mal in einem separaten Projekt eingebaut und auch eine weitere Klasse Multiplikation hinzugefügt.

Damit funktioniert das auch soweit.

 

Zu dem Logger habe ich noch ein paar Fragen: Ich habe so was noch nicht gemacht. Wo schreibt der Logger denn hin? In eine Textdatei? Ist der Logger eine Komponente, die ich hinzufügen muss?

Muss ich diese Klasse in der Form über einen Timer zyklisch mit Parameter versorgen?   

Wenn ich die Klasse LoggableCalculation programmiere, zeigt er mir in der Fehlerliste an, das der Typ Logger nicht definiert ist.

Gruß

Eleu

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 22 Minuten schrieb Eleu:

Ich glaube ich habe das soweit verstanden. Die Klasse LoggableCalculation erbt die Funktionen von der Klasse ICalculation. Die Klasse BinaryCalculation, erbt ebenfalls von der Klasse ICalculation. Darüber erfolgt die Trennung und die Funktion kann sich zur Laufzeit ändern

ICalculation ist keine Klasse, sondern ein Interface. Ein Interface definiert die Schnittstelle nach außen. Es ist quasi eine leere Hülle und gibt die Funktionen vor, die eine Klasse implementiert haben müssen. Deswegen ist es in meinem Beispiel dem Logger egal, was für eine Berechnung dahintersteckt, da alle Berechnungen das Interface implementieren und somit alle eben die Funktion Calculate() haben.

vor 22 Minuten schrieb Eleu:

Zu dem Logger habe ich noch ein paar Fragen: Ich habe so was noch nicht gemacht. Wo schreibt der Logger denn hin? In eine Textdatei? Ist der Logger eine Komponente, die ich hinzufügen muss?

Muss ich diese Klasse in der Form über einen Timer zyklisch mit Parameter versorgen?   

Wenn ich die Klasse LoggableCalculation programmiere, zeigt er mir in der Fehlerliste an, das der Typ Logger nicht definiert ist.

Der Logger war von mir nur frei erwunden. Es gibt den in dieser Form nicht. Er war von mir nur ein abstraktes Beispiel, um die Prinzipien zu verdeutlichen und was passiert, wenn man weitere Features hinzufügen möchte. Wenn du etwas loggen willst, kannst du natürlich einen eigenen Logger schreiben. In Visual Basic bin ich aber leider nicht so firm drinnen, da ich mit C# arbeite aber ich denke mal, für Visual Basic gibt es auch schon fertige OpenSource-Logger, die man verwenden kann. Für C# gibt es z.B. NLog und NLog bietet von Haus aus über 30 verschiedene Möglichkeiten, etwas zu loggen (z.B. auf die Konsole, als Textdatei, als E-Mail, in eine Datenbank, in Slack, ...) und wenn das nicht reicht, der kann weitere hinzuprogrammieren.

Bearbeitet von Whiz-zarD
Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Stunden schrieb Whiz-zarD:

In Visual Basic bin ich aber leider nicht so firm drinnen, da ich mit C# arbeite aber ich denke mal, für Visual Basic gibt es auch schon fertige OpenSource-Logger, die man verwenden kann.

Da wir von VB.NET reden, kannst du jede .NET-Logging-Bibliothek verwenden. Neben NLog gibt's da z.B. noch log4net oder serilog (wobei letzteres etwas ungewöhnlicher ist, aber auch sehr spannend).

Link zu diesem Kommentar
Auf anderen Seiten teilen

Am ‎09‎.‎03‎.‎2018 um 08:47 schrieb Whiz-zarD:

ICalculation ist keine Klasse, sondern ein Interface. Ein Interface definiert die Schnittstelle nach außen. Es ist quasi eine leere Hülle und gibt die Funktionen vor, die eine Klasse implementiert haben müssen. Deswegen ist es in meinem Beispiel dem Logger egal, was für eine Berechnung dahintersteckt, da alle Berechnungen das Interface implementieren und somit alle eben die Funktion Calculate() haben.

 

Hallo Whiz-zarD,

dann habe ich es wohl doch noch nicht kapiert.

Mal angenommen, ich möchte den Rückgabewert der Klasse Addieren (Also das Ergebnis der Berechnung) in eine Textdatei schreiben.

Dieses könnte ich einfach tun, indem ich dafür eine Prozedur in der Form programmiere. Mit dem auslösenden Ereignis für die Berechnung (Button) würde ich dieser Prozedur, den Rückgabewert übergeben.

Ich verstehe das ganze Konstrukt von dir nun so, dass dieses auch von der Klasse LoggableCalculation durchgeführt werden könnte, weil es ja über das Interface ICalculation die Berechnung der Klassen Addieren, Multiplizien, etc. erbt.

Ich habe einiges versucht, aber es ist mir nicht gelungen, die Klasse LoggableCalculation dahingehend umzuprogrammieren, das sie die Ergebnisse der unterlagerten Klassen in eine Textdatei speichert.

Gruß

Eleu

Link zu diesem Kommentar
Auf anderen Seiten teilen

Um es noch mal zu verdeutlichen, habe ich noch mal den Code der Form angehangen, um mit dem Button das Ergebnis der Klasse Addition in eine Textdatei zu schreiben.

Public Class Form1
    Dim Addi As New Addition(2, 5)
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'Rückgabewert der Klasse Addition in TextBox anzeigen
        TextBox1.Text = Addi.Calculate
        'Rückgabewert der Klasse Addition der Prozedur "Ereignis speichern" übergeben
        Ergebnis_speichern(Addi.Calculate)
    End Sub
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'Textdatei mit dem Start der Appklikation erzeugen
        System.IO.File.WriteAllText("C:\VB_NET\Test.txt", "")
    End Sub
    Private Sub Ergebnis_speichern(ByVal Ergeb As String)
        ' Schreibe das Ergebnis in die Textdatei
        Using writer As New IO.StreamWriter("C:\VB_NET\Test.txt", True)
            writer.WriteLine(Ergeb)
        End Using
    End Sub
End Class

Ich stelle mir nun so vor, dass die Prozedur "Ergebnis_speichern" in die Klasse "LoggableCalculation" wandert und es von dort ausgeführt wird, wenn der Button in der Form betätigt wird. Wie müsste das programmiert werden?

Gruß

Eleu

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 9 Stunden schrieb Eleu:

Mal angenommen, ich möchte den Rückgabewert der Klasse Addieren (Also das Ergebnis der Berechnung) in eine Textdatei schreiben.

Dieses könnte ich einfach tun, indem ich dafür eine Prozedur in der Form programmiere. Mit dem auslösenden Ereignis für die Berechnung (Button) würde ich dieser Prozedur, den Rückgabewert übergeben.

Viele Wege führen nach Rom. ;)

Allerdings ist das Problem von WinForms-Anwendungen, dass sie den Entwickler nicht an "die Hand nehmen". Eine Form sollte eigentlich nur die Logik besitzen, die auch für die Anzeige notwendig ist. Siehe das MVC (Model-View-Controller) bzw. MVP (Model-View-Presenter) oder für WPF-Anwendungen das MVVM (Model-Viel-ViewModel)-Pattern. Da es aber so schön einfach ist, ein Button in die Form per Drag'n'Drop in die Form zu schieben und dann mit einem Doppelklick das Event zu implementieren, sind diese Patterns nicht so ganz ersichtlich. Bei den Patterns wird die grafische Oberfläche von der Geschäftslogik getrennt. Der Grund ist einfach der, wenn man die Geschäftslogik von der grafischen Oberfläche trennt, kann man die grafische Oberfläche recht leicht austauschen. Angenommen du entwickelst eine WinForms-Anwendung und willst sie später in eine Web-Applikation überführen. Wenn die Geschäftslogik aber nun in der grafischen Oberfläche steckt, müsstest du die gesamte Anwendung neu schreiben. Lagert man sie hingegen in eine separate Klasse aus, so braucht man letztendlich im Idealfall nur die grafische Oberfläche austauschen. Man lagert also die Geschäftslogik in eine separate Klasse (der Controller) aus und die grafische Oberfläche (die View) kommuniziert über eine Schnittstelle mit der Geschäftslogik. 

Ein weiterer Grund für das Auslagern der Geschäftslogik ist wieder die Testbarkeit. Wenn du das Logging automatisch per Unittest testen möchtest, musst du wiederrum die Form-Klasse erzeugen und ein Klick auf den Button simulieren. Da die Events private sind, kannst du die Events nicht von Außen aufrufen. Dann muss man wieder andere Tools verwenden, die eine Anwendung automatisch starten und testen kann oder man muss mittels Reflection das Event ausführen aber das alles verkompliziert den Test und sollte vermieden werden und ist auch sehr fehleranfällig. Liegt das Logging aber in einer separaten Klasse, die von außen steuerbar ist, ist der Unittest mit drei Zeilen Code erledigt.

Die LoggableCalculation-Klasse ist im Grunde auch nur ein Kompromiss, weil die Calculate()-Funktion nicht nur das Ergebnis berechnet und zurückgibt, sondern auch das Ergebnis loggt und somit auch gegen das Single-Responsibility-Prinzip verstößt aber es ist in dem Sinne ein Kompromiss, weil die Berechnung in einer separaten Klasse liegt und die LoggableCalculation-Klasse erweitert die Berechnung ums Logging. Dieses Vorgehen nennt sich auch Decorator-Pattern.

Wenn du in deinem Beispiel das Ergebnis der Addition in eine Textdatei speichern möchtest, müsstest du im Grunde nur folgende Zeile 

Dim Addi As New Addition(2, 5)

gegen

Dim logger As New FileLogTraceListener With {
    .CustomLocation = "D:\",
    .BaseFileName = "test"
}

Dim LoggableCalculation As New LoggableCalculation(New Addition(2, 5), logger)

austauschen. Visual Basic besitzt schon mehrere integrierte Logger. Der FileLogTraceListener ist ein Logger, der in eine Datei schreibt. Einen Standard-Logger könnte man über die app.config-Datei konfigurieren und per 

My.Application.Log

aufrufen aber ich erzeuge hier mal eine eigene Instanz von einem Logger, weil es sonst zu weit führen würde. Die LoggableCalculation-Klasse sieht dann wie folgt aus:

Public Class LoggableCalculation
    Inherits ICalculation

    Private ReadOnly calculation As ICalculation
    Private ReadOnly logger As TraceListener

    Public Sub New(calculation As ICalculation, logger As TraceListener)
        Me.calculation = calculation
        Me.logger = logger
    End Sub

    Public Overrides Function Calculate() As Double
        Dim result As Double
        result = Me.calculation.Calculate()
        Me.Log(result)
        Return result
    End Function

    Private Sub Log(result As Double)
        logger.WriteLine(result)
    End Sub
End Class

Wenn du dann die Calculate()-Funktion aufrufst, wird dann unter D:\ die Datei test.log erzeugt. Den Pfad und den Dateinamen kannst du im oberen Code anpassen. Wie du dann siehst, ist das Erzeugen der Log-Datei komplett unabhängig von der Form und ich könnte den Code somit auch z.B. in einer Konsolen-Anwendung verwenden:

Sub Main()
    Dim logger As New FileLogTraceListener With {
        .CustomLocation = "D:\",
        .BaseFileName = "test"
    }

    Dim LoggableCalculation As New LoggableCalculation(New Addition(5, 3), logger)
    Console.WriteLine(LoggableCalculation.Calculate())
    Console.ReadKey()
End Sub

Oder als Test (hier als Beispiel mit dem Unittest Framework von Microsoft):

<TestClass()> Public Class UnitTest1

    <TestMethod()> Public Sub Five_Plus_Two()
        Dim calculation As New Addition(5, 2)
        Assert.AreEqual(7.0, calculation.Calculate())
    End Sub

    <TestMethod()> Public Sub Log_Five_Plus_Two()
        Dim calculation As New Addition(5, 2)
        Using logger As TraceListener = CreateTraceListener()
            Dim LoggableCalculation As New LoggableCalculation(calculation, logger)
            LoggableCalculation.Calculate()
        End Using

        Assert.AreEqual(True, File.Exists("D:\test.log"))
        Assert.AreEqual($"7{Environment.NewLine}", File.ReadAllText("D:\test.log"))
    End Sub

    Private Function CreateTraceListener() As TraceListener
        Return New FileLogTraceListener With {
            .CustomLocation = "D:\",
            .BaseFileName = "test",
            .Append = False
        }
    End Function

End Class

Ich kann hier also die gesamte Funktionalität testen, ohne dabei die Anwendung starten und auf den Button drücken zu müssen. Die Prozedur Five_Plus_Two() überprüft, ob das Ergebnis von 5 + 2 = 7 ist und Log_Five_Plus_Two() überprüft, ob eine Log-Datei geschrieben wurde und ob dort 7 drinnensteht. Unter Visual Studio sieht es dann so aus:Unittest.png.f4a7f96583d26374659ff65630e9021a.png

So kann man auf Knopfdruck etliche Tests durchführen, ohne einmal die Anwendung gestartet zu haben. Das erleichtert das Leben erheblich, weil man so schnell Feedback bekommt, ob die Geschäftslogik funktioniert. Man muss sich dann nicht mühselig durch alle Buttons und Menüs klicken. Ob der Button allerdings das tut, was er auch soll, muss dann aber per manuellen Test herausgefunden werden aber ich habe die darunterliegende Funktionalität per Unittest getestet.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo Whiz-zarD,

also wenn ich deinen Code 1:1 so verwende, bekomme ich beim FileLogTraceListener die Fehlermeldung Typ nicht definiert.

Ist aber auch kein Problem, da ich dank deiner umfangreichen Ausführungen, nun eine Klasse Ergebnisse_speichern erstellen konnte, die die Ergebnisse der Klassen Addition und Multiplikation in eine weitere Textdatei abspeichert.

Ich habe in der Form zwei TextBoxen für die Eingabe der beiden Werte die addiert, oder multipliziert werden sollen ergänzt.

Der Code in der Form sieht nun so aus:

Public Class Form1
    Dim Wert1 As Double = 0
    Dim Wert2 As Double = 0
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim Addi As New Addition(Wert1, Wert2)
        Dim Ergebnisse_speichern As New Ergebnisse_speichern(New Addition(Wert1, Wert2))
        'Rückgabewert der Klasse Addition in TextBox anzeigen
        TextBox1.Text = Addi.Calculate
        'Rückgabewert der Klasse Addition der Prozedur "Ereignis speichern" übergeben
        Ergebnis_speichern(Addi.Calculate)
        ' Die Klasse Ergebnisse_speichern ausführen
        Ergebnisse_speichern.Calculate()
    End Sub
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'Textdatei mit dem Start der Appklikation erzeugen
        System.IO.File.WriteAllText("C:\VB_NET\Test.txt", "")
        'Textdatei mit dem start der Applikation für die Klasse Ergebisse_speichern erzeugen
        System.IO.File.WriteAllText("C:\VB_NET\Test3.txt", "")
    End Sub
    Private Sub Ergebnis_speichern(ByVal Ergeb As String)
        ' Schreibe das Ergebnis in die Textdatei
        Using writer As New IO.StreamWriter("C:\VB_NET\Test.txt", True)
            writer.WriteLine(Ergeb)
        End Using
    End Sub
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim Multi As New Multiplikation(Wert1, wert2)
        Dim Ergebnisse_speichern2 As New Ergebnisse_speichern(New Multiplikation(Wert1, wert2))
        'Rückgabewert der Klasse Multiplikation in TextBox anzeigen
        TextBox1.Text = Multi.Calculate
        'Rückgabewert der Klasse Multiplikation der Prozedur "Ereignis speichern" übergeben
        Ergebnis_speichern(Multi.Calculate)
        ' Die Klasse Ergebnisse_speichern ausführen
        Ergebnisse_speichern2.Calculate()
    End Sub
    Private Sub TextBox2_TextChanged(sender As Object, e As EventArgs) Handles TextBox2.TextChanged
        '1´te Werteingabe in TextBox 
        Wert1 = Val(TextBox2.Text.Replace(",", "."))
    End Sub
    Private Sub TextBox3_TextChanged(sender As Object, e As EventArgs) Handles TextBox3.TextChanged
        '2te Werteingabe in Textbox
        Wert2 = Val(TextBox3.Text.Replace(",", "."))
    End Sub
End Class

Die Klasse Ergebnisse_speichern sieht so aus:

Public Class Ergebnisse_speichern
    Inherits ICalculation
    Private ReadOnly calculation As ICalculation
    Public Sub New(calculation As ICalculation)
        Me.calculation = calculation
    End Sub
    Public Overrides Function Calculate() As Double
        Dim result As Double
        result = Me.calculation.Calculate()
        Me.Log(result)
        Return result
    End Function
    Private Sub Log(result As Double)
        Using writer As New IO.StreamWriter("C:\VB_NET\Test3.txt", True)
            writer.WriteLine(result)
        End Using
    End Sub
End Class

Damit klappt es nun auch die Ergebnisse über die Klasse wegzuspeichern.

Vielen, vielen Dank für deine Hilfe.

Gruß

Eleu

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo,

vielleicht doch noch eine Frage.

Warum brauche ich die Klasse "BinaryCalculation"

Wenn ich z.B. die Klasse Addition so umprogrammiere, kann ich die Klasse "BinaryCalculation" löschen:

Public Class Addition
    Inherits ICalculation
    Private ReadOnly calculation As ICalculation
    Private ReadOnly left As Double
    Private ReadOnly right As Double
    Public Sub New(calculation As ICalculation)
        Me.calculation = calculation
    End Sub
    Public Sub New(left As Double, right As Double)
        Me.left = left
        Me.right = right
    End Sub
    Public Overrides Function Calculate() As Double
        Return Me.left + Me.right
    End Function
End Class

Gruß

Eleu 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Es geht um das DRY-Prinzip (Don't Repeat yourself). Wenn du nicht nur die Addition implementieren willst, sondern auch noch weitere Rechenoperationen, dann brauchen sie ebenfalls left und right. Damit man beide Variablen nicht für jede Operation kopieren muss, habe ich sie in eine abstrakte Klasse ausgelagert und jede Operation erbt von dieser Klasse. So braucht man die beiden Variablen nur ein mal definieren.

Abstrakte Klassen sind nicht instanziierbar. Du kannst also kein

new BinaryCalculation()

aufrufen. Das führt sofort zu ein Kompilierfehler. Abstrakte Klassen dienen für eine Basisimplementierung einer Funktionalität. Klassen, die dann von der abstrakten Klasse erben, implementieren dann die konkrete Fachlogik. Die Subtraction-Klasse wird dann einfach so aussehen:

Public Class Subtraction
    Inherits BinaryCalculation

    Public Sub New(left As Double, right As Double)
        MyBase.New(left, right)
    End Sub

    Public Overrides Function Calculate() As Double
        Return Me.left - Me.right
    End Function
End Class

left und right braucht man ja nicht noch mal definieren, weil sie eben schon in BinaryCalculation definiert worden sind.
 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo Whiz-zarD,

vielen Dank.

Mittlerweile habe ich mir meine Ausgangsfrage aus diesem Thread selber beantworten können:

Klasse für das Addieren:

{
    public class Addition 
    {
        private double left;
        private double right;
        public Addition(double left, double right)
        {
            this.left = left;
            this.right = right;
        }
        public double Calculate()
        {
            return (this.left + this.right);
        }
    }
}

Übergabe der Parameter in der Form:

{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            Addition Addi = new Addition(2, 5);
    
            double s  = Addi.Calculate();
            textBox1.Text = Convert.ToString(s);
        }
    }
}

Aber wie müsste dann eine Basisklasse (Interface) und die Klasse Ergebnisse_speichern aussehen, damit man das Ergebnis in eine Textdatei bekommt?

Gruß

Eleu

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich schreibe das jetzt mal in C#, weil ich VB nicht kann:

Du kannst das grundsätzlich beliebig kompliziert machen, aber für einen ersten Wurf fände ich folgende Definitionen sinnvoll:

public interface ICalculation
{
 double Calculate(); 
}

public abstract class BinaryCalculation : ICalculation
{
  protected double _firstOperand;
  protected double _secondOperand;
  public BinaryCalculation(double a, double b)
  {
     _firstOperand = a;
     _secondOperand = b;
  }
  
  public abstract double Calculate();
}

public class Addition : BinaryCalculation
{
 public Addition(double a, double b) : base(a, b) { }
  
 public override double Calculate() => _firstOperand + _secondOperand;
}

So, und jetzt kannst du kreativ werden. Ich könnte mir bspw. so etwas vorstellen:

public class ResultWriter
{
  private string _file;
  public ResultWriter(string path)
  {
	_file = path; // Fehlerbehandlung lasse ich hier mal raus
  }

  public void WriteResultToFile(ICalculation calculation)
  {
 	File.AppendAllText($"{calculation.GetType()} result: {calculation.Calculate()}");
  }
}

Du kannst das aber auch in die Konsole schreiben, oder in eine Datenbank, oder oder oder... (und natürlich kannst du auch hier ein Interface definieren, das nur die Methode "SaveCalculationResult(ICalculation calculation)" hat).

(Und statt "calculation.GetType()" könntest du in ICalculation natürlich auch eine Methode "GetDescription()" vorsehen, die dir das in schön ausgibt.)

Bearbeitet von arlegermi
Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo alergermi,

vielen Dank dafür, aber leider klappt es nicht bei mir. Wahrscheinlich sind meine C# Kenntnisse nicht ausreichend?

In der Zeile

File.AppendAllText($"{calculation.GetType()} result: {calculation.Calculate()}");

bekomme ich bei AppendAllText die Fehlermeldung:

Keine Überladung für die AppendAllText Methode nimmt 1 Argument ein.

Des Weiteren weiß ich nicht, wie ich in dem Button für die Berechnung nun WriteResultToFile programmieren muss?

  ResultWriter Speichern = new ResultWriter("C:\\VB_net\\Test.txt");
              
  Speichern.WriteResultToFile(Was muss hier rein? Erwartet wird ICalculation calculation);

Gruß

Eleu

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 17 Minuten schrieb Eleu:

File.AppendAllText($"{calculation.GetType()} result: {calculation.Calculate()}");

 

Siehe: https://msdn.microsoft.com/de-de/library/ms143356(v=vs.110).aspx

Dir fehlt die Angabe der Datei in der du den Text schreiben möchtest.

Müsste also ungefähr wie folgt aussehen:

// public static void AppendAllText(string path,string contents)
File.AppendAllText(_file, $"{calculation.GetType()} result: {calculation.Calculate()}");

 

vor 17 Minuten schrieb Eleu:

Des Weiteren weiß ich nicht, wie ich in dem Button für die Berechnung nun WriteResultToFile programmieren muss?


  ResultWriter Speichern = new ResultWriter("C:\\VB_net\\Test.txt");
              
  Speichern.WriteResultToFile(Was muss hier rein? Erwartet wird ICalculation calculation);

Gruß

Da musst du ein Objekt  übergeben welches das ICalculation Interface implementiert.
In deinem Fall wäre das eine Instanz der BinaryCalculation Addition Klasse:

Speichern.WriteResultToFile(new Addition(zahl1, zahl2));

Die Methode erwartet "nur" ein Objekt welches das ICalculation Interface implementiert.
Das kann entweder deine BinaryCalculation Addition Klasse sein, oder eben eine andere Objektinstanz einer Klasse die dieses Interface implementiert.

Bearbeitet von r4phi
Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 12 Minuten schrieb r4phi:

 


Speichern.WriteResultToFile(new Addition(zahl1, zahl2));

Die Methode erwartet "nur" ein Objekt welches das ICalculation Interface implementiert.
Das kann entweder deine BinaryCalculation Addition Klasse sein, oder eben eine andere Objektinstanz einer Klasse die dieses Interface implementiert.

Hi,

das habe ich schon versucht.

Z.B: 

 Speichern.WriteResultToFile(new Addition(4,5));

Aber wenn ich dann den Button ausführe, steht in der Textdatei folgender Eintrag:

{calculation.GetType()} result: {calculation.Calculate()}

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 5 Minuten schrieb Eleu:

Aber wenn ich dann den Button ausführe, steht in der Textdatei folgender Eintrag:

Welche Version von Visual Studio nutzt du? Soviel ich weiß wird die String Interpolation erst ab Visual Studio 2015 unterstützt.
Alternativ Versuch folgendes:

public void WriteResultToFile(ICalculation calculation)
  {
 	File.AppendAllText($"{calculation.GetType()} result: {calculation.Calculate()}");
  }

durch das ersetzen:

public void WriteResultToFile(ICalculation calculation)
  {
 	File.AppendAllText(_file, string.Format("'{0}' result: {1}", calculation.GetType(), calculation.Calculate()));
  }

 

Bearbeitet von r4phi
Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 16 Minuten schrieb Eleu:

Hi,

ich verwende Visual Studio 2015, allerdings die kostenlose Variante, die man hier downloaden kann:

 

Wobei, mir fällt ein:
Wenn du
 

$"{calculation.GetType()} result: {calculation.Calculate()}"

benutzt und dein Visual Studio String Interpolation nicht unterstützt dann sollte diese Zeile rot unterkringelt sein und das Projekt sollte sich nicht kompilieren lassen.

Kannst du das ganze noch mal probieren?
Wichtig ist dass das $ Zeichen vor den Anführungsstrichen steht.

Folgende Aussage

vor 29 Minuten schrieb Eleu:

Aber wenn ich dann den Button ausführe, steht in der Textdatei folgender Eintrag:

{calculation.GetType()} result: {calculation.Calculate()}


liest sich nämlich so als würde das $ vor den Anführungszeichen fehlen, da eigentlich der Code in den geschweiften Klammern ausgeführt werden muss / soll und das Ergebnis dieses Codes dort als string ausgegeben wird.

Bearbeitet von r4phi
Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo r4phi,

ich habe jetzt folgendes gemacht:

 public void WriteResultToFile(ICalculation calculation)
        {
            File.AppendAllText(_file, $"{calculation.GetType()} result: {calculation.Calculate()}");
        }

Du hast recht, die Zeile ist nach dem Einfügen des  $ Zeichen nicht mehr rot, aber wenn ich den Button ausführe steht in der Textdatei:

Klassenprogrammierung_in_C_Sharp.Addition result: 9

Edit: Ach so, das soll auch dabei rauskommen?

Bearbeitet von Eleu
Link zu diesem Kommentar
Auf anderen Seiten teilen

Gerade eben schrieb Eleu:

Zeichen nicht mehr rot, aber wenn ich den Button ausführe steht in der Textdatei:

Klassenprogrammierung_in_C_Sharp.Addition result: 9

Perfekt. Dann funktioniert es nun wie es soll.
Klassenprogrammierung_in_C_Sharp.Addition  => calculation.GetType() (Der Laufzeittyp der Objektinstanz.)
result: 9  => die 9 entspricht dem Rückgabewert von calculation.Calculate()

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...