Entwickler-Ecke

Sonstiges (.NET) - Grundlagen OOP - Projekt Taschenrechner


OldCat - Sa 04.12.21 15:58
Titel: Grundlagen OOP - Projekt Taschenrechner
Dieser Thread beschäftigt sich mit den Grundlagen zu: "objektorientierter Programmierung", kurz: OOP.

Er findet seinen Ursprung in der Frage: "Wozu in einer (objektorientierten) Programmiersprache denn überhaupt Rückgabewerte notwendig seien."
Rückgabewerte - Wofür brauche ich sie? [https://entwickler-ecke.de/topic_Rueckgabewerte++wofuer+brauche+ich+sie_118862.html]

Um mir - einem Anfänger - den Sinn von Rückgabewerten vor Augen zu führen, wurde mir vorgeschlagen, mal einen einfachen Taschenrechner zu programmieren.Die Vorgaben dazu waren:
  1. Das Programm soll Rückgabewerte nutzen.
  2. Das Programm soll in Eingabe, Verarbeitung, Ausgabe unterteilt sein.
  3. Die Unterteilung in E.V.A. soll idealerweise in eigenen Klassen und deren Methoden strukturiert sein.

In meinem Code verzichte ich (auf Anraten) auf using Direktiven, um mir als Anfänger nicht die "Herkunft" von Klassen und Methoden der .NET Bibliothek zu verschleiern.
Mein aktueller Quellcode ist noch stark verbesserungswürdig.

Main Class:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
// using System;

// Ich versuche es, Klassen und Methoden modular aufzubauen.
namespace Taschenrechner2
{
    internal class MainClass
    {
        static void Main(string[] args)
        {
            RunProgram myCalc = new RunProgram();
            myCalc.Run();
        }
    }
}


RunProgram Class:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
namespace Taschenrechner2
{
    internal class RunProgram
    {
        private const string AppTitle = "Mini Calculator";
        public void Run()
        {   System.Console.Title = AppTitle;

            System.Console.WriteLine("###############");
            System.Console.WriteLine(AppTitle);
            System.Console.WriteLine("###############\n");

            CalcInput.ChooseInputs();

            System.Console.WriteLine("\nPress any key to exit...");
            System.Console.ReadKey();
        }
    }
}


CalcInput Class:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
namespace Taschenrechner2
{
    internal class CalcInput
    {
        public static void ChooseInputs()
        {
            double result = 0;
            char userDecision;
            do
            {
                System.Console.WriteLine("Please enter your first digit: ");
                double digit1 = double.Parse(System.Console.ReadLine());
                System.Console.WriteLine($"You chose: {digit1}");

                System.Console.WriteLine("Please enter your second digit: ");
                double digit2 = double.Parse(System.Console.ReadLine());
                System.Console.WriteLine($"You chose: {digit2}");

                System.Console.WriteLine(@"Choose your operator by number and hit enter:

> 1.) Addition
> 2.) Subtraction
> 3.) Multiplication
> 4.) Division"
);

                int userChoice = int.Parse(System.Console.ReadLine());

                switch (userChoice)
                {
                    case 1:
                        result = Calculation.Addition(digit1, digit2);
                        break;
                    case 2:
                        result = Calculation.Subtraction(digit1, digit2);
                        break;
                    case 3:
                        result = Calculation.Multiplication(digit1, digit2);
                        break;
                    case 4:
                        result = Calculation.Division(digit1, digit2);
                        break;
                    default:
                        System.Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
                        ChooseInputs();
                        break;
                }
                System.Console.WriteLine(result);

                System.Console.WriteLine("Do you like to solve another arithmetical problem? (y/n)");
                userDecision = char.Parse(System.Console.ReadLine().Trim().ToLower());
            } while (userDecision == 'y');
        }
    }
}


Calculation Class:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
namespace Taschenrechner2
{
    internal class Calculation
    {
        public static double Addition(double x, double y)
        {
            return x + y;
        }

        public static double Subtraction(double x, double y)
        {
            return x - y;
        }

        public static double Multiplication(double x, double y)
        {
            return x * y;
        }

        public static double Division(double x, double y)
        {
            return x / y;
        }
    }
}


Dieser Code hat immer noch mehrere Schwächen:
Zitat:
user profile iconTh69 Es gibt trotzdem noch einen logischen Fehler - und zwar der rekursive Aufruf von ChooseInputs(): gib mal zuerst eine falsche Operatorauswahl ein (z.B. 5) und danach eine richtige, dann beende die Schleife (mit z.B. n) und beobachte die Ausgabe danach.
Um das zu beheben ist es eben sinnvoll, die einzelnen Codeteile in eigene Methoden zu gliedern (und nur das aufzurufen, was man auch haben will - hier also die Eingabe) - aber hier würde es besser sein, die Schleife zu wiederholen, anstatt der Rekursion.

PS: Der char.Parse-Aufruf ergibt eine FormatException, falls der Benutzer mehrere Zeichen (z.B. "yes" oder "no") eingibt und das sollte es wohl nicht - frag einfach das erste Zeichen ab (falls die Eingabe nicht leer ist) - aber wenigstens hast du ja auch hier den Rückgabewert in einer Variablen gespeichert ;-).

Zitat:
user profile iconPalladin007 Allerdings würde ich es aufsplitten.

Z.B. könntest Du den Inhalt der while-Schleife als eigene Methode herauslösen, die dann alles für genau eine Berechnung (Eingaben, Rechnen, Ausgabe) enthält.
Die zweite Methode enthält die while-Schleife, die Abfrage, ob man weiter machen möchte und den Aufruf der Methode mit der eigentlichen Arbeit.
Du erreichst dann zwei kleinere Methoden, die dadurch übersichtlicher sind.


Dann mal an die Arbeit :lupe:


Moderiert von user profile iconTh69: Topic aus C# - Die Sprache verschoben am Sa 04.12.2021 um 15:44


Palladin007 - Sa 04.12.21 16:03

Zitat:
Dann mal an die Arbeit


Ja, ran an die Arbeit :D
Wo ist dein Versuch, die Probleme anzugehen? Wo kommst Du nicht weiter, was verstehst Du nicht?
Der Code sieht unverändert aus.


OldCat - So 05.12.21 13:39

Wünsche allen einen gemütlichen 2. Advent. Ganz unabhängig davon, ob ihr dem Licht der Vernunft oder einem orientalischen Gott folgt :D

Hier mein aktueller Code:

CalcInput Klasse:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
namespace Taschenrechner2
{
    internal class CalcInput
    {
        public static void UserChoiceLoop()
        {
            string userDecision;
            do
            {
                UserInputs();

                System.Console.WriteLine("Do you like to solve another arithmetical problem? (yes/no)");
                userDecision = System.Console.ReadLine().Trim().ToLower();
            } while (userDecision.StartsWith('y'));
        }

        private static void UserInputs()
        {
            System.Console.WriteLine("Please enter your first digit: ");
            double digit1 = double.Parse(System.Console.ReadLine());
            System.Console.WriteLine($"You chose: {digit1}");

            System.Console.WriteLine("Please enter your second digit: ");
            double digit2 = double.Parse(System.Console.ReadLine());
            System.Console.WriteLine($"You chose: {digit2}\n");

            CalcChoice(digit1, digit2);
        }

        private static void CalcChoice(double digit1, double digit2)
        {
            double result = 0;

            System.Console.WriteLine(@"Choose your operator by number and hit enter:

> 1.) Addition
> 2.) Subtraction
> 3.) Multiplication
> 4.) Division"
);

            int userChoice = int.Parse(System.Console.ReadLine());

            switch (userChoice)
            {
                case 1:
                    result = Calculation.Addition(digit1, digit2);
                    break;
                case 2:
                    result = Calculation.Subtraction(digit1, digit2);
                    break;
                case 3:
                    result = Calculation.Multiplication(digit1, digit2);
                    break;
                case 4:
                    result = Calculation.Division(digit1, digit2);
                    break;
                default:
                    System.Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
                    break;
            }

            // System.Console.WriteLine(result);
            CalcOutput.OutputValue(result);
        }

        //        public static void ChooseInputs()
        //        {
        //            double result = 0;
        //            string userDecision;
        //            do
        //            {
        //                System.Console.WriteLine("Please enter your first digit: ");
        //                double digit1 = double.Parse(System.Console.ReadLine());
        //                System.Console.WriteLine($"You chose: {digit1}");

        //                System.Console.WriteLine("Please enter your second digit: ");
        //                double digit2 = double.Parse(System.Console.ReadLine());
        //                System.Console.WriteLine($"You chose: {digit2}");

        //                System.Console.WriteLine(@"Choose your operator by number and hit enter:

        //> 1.) Addition
        //> 2.) Subtraction
        //> 3.) Multiplication
        //> 4.) Division");

        //                int userChoice = int.Parse(System.Console.ReadLine());

        //                switch (userChoice)
        //                {
        //                    case 1:
        //                        result = Calculation.Addition(digit1, digit2);
        //                        break;
        //                    case 2:
        //                        result = Calculation.Subtraction(digit1, digit2);
        //                        break;
        //                    case 3:
        //                        result = Calculation.Multiplication(digit1, digit2);
        //                        break;
        //                    case 4:
        //                        result = Calculation.Division(digit1, digit2);
        //                        break;
        //                    default:
        //                        System.Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
        //                        ChooseInputs();
        //                        break;
        //                }
        //                System.Console.WriteLine(result);
        //                // CalcOutput.OutputValue(result);

        //                System.Console.WriteLine("Do you like to solve another arithmetical problem? (yes/no)");
        //                userDecision = System.Console.ReadLine().Trim().ToLower();
        //            } while (userDecision.StartsWith('y'));

        //        }
    }
}


CalcOutput Klasse:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
namespace Taschenrechner2
{
    internal class CalcOutput
    {
        public static void OutputValue(double result)
        {
            System.Console.WriteLine(result);
        }
    }
}


Palladin007 - Mo 06.12.21 09:33

Was für einen Sinn hat die CalcOutput-Klasse?

Und wenn Du direkt mit den Prinzipien starten willst, dann ließ dich mal in das Single-responsibility principle ein.
Es gibt viele teils sehr verschiedene Erklärungen (nur eine Aufgabe, ein Grund sich zu ändern, eine Verantwortung, etc.), sie alle meinen aber grob das gleiche.
Das betreffend gibt's bei dir mehrere Probleme, ob sie für einen blutigen Anfänger relevant sind - naja, kann man drüber streiten, ich halte das SRP für so essenziell, zu früh geht nicht ^^
SRP ist übrigens Teil der S.O.L.I.D.-Prinzipien, alle sehr wichtig, aber die anderen sind noch schwerer zu verstehen und mMn. nicht ganz so super wichtig.

Die Probleme kann man aber von verschiedenen Winkeln betrachten, ich versuche es mal von einem anderen (nicht SRP) Winkel:
Stell dir immer vor, jemand Fremdes ließt den Code einer (!!!) Methode, will nicht immer alle anderen Methoden nachschauen (bei großen Projekten geht das gar nicht).
Besagter Fremder muss die Methode also ohne Hilfe und nur anhand der Namen verstehen können.

Was würde der Fremde denken, tut die UserInputs-Methode?
Ich würde nicht auf die Idee kommen, dass diese Methode auch noch berechnet ;)
Ich persönlich würde sie aufteilen: Eine Methode, die einen Wert abfragt und per Parameter stellst Du ein, wie dieser Wert in der Konsole heißen soll.
Das ist dann auch direkt nützlich, wenn Du dafür sorgen willst, dass dein Programm nicht abstürzt, wenn der Nutzer "a" statt "1" eintippt, das musst Du dann nur einmal machen und nicht zwei Mal.

Und die CalcChoice-Methode: Wie soll sie denn berechnen und was passiert damit? Das verrät der Name nicht.
Richtig heißen müsste sie also "AskForChoiceAndCalcResultAndPrintToConsole", was direkt sehr viel schlechter zu lesen ist und - spoiler - auch nicht der optimale Weg.
Da versteckt sich auch direkt ein sehr oft passender Indiz für verletztes SRP: "And" im Namen, zu lange Namen oder generell Schwierigkeiten, einen einfach verständlichen Namen zu finden.

Besser wäre hier, Du teilst wieder auf:


Da hast Du dann auch gleich ein paar mehr Rückgabenwerte

PS:

"UserChoiceLoop" ist auch kein guter Name.
Bei so einem Projekt würde ich den Inhalt einfach in der Main-Methode lassen.
Eine beliebige Namenskonvention ist aber auch, dass die Klasse "Application" und die Methode "Run" heißt.
Bei solchen Methoden kann man keinen klaren Namen wählen, deshalb betrachtet man es von weiter oben, mit dem Hintergedanken, dass der Leser gar nicht genau wissen muss, was darin passiert bzw. so oder so nachlesen muss.


OldCat - Mo 06.12.21 12:43

Hey Palladin007,

danke für Deine weiterführende Hilfe :beer:

Bevor ich Deine Vorschläge umsetze, möchte ich sichergehen, dass ich Dich auch verstanden habe. Daher werde ich jetzt noch einmal eine paar Fragen dazu stellen, bzw. Deinen Text noch einmal durchgehen:

Zitat:
Was für einen Sinn hat die CalcOutput-Klasse?

Aus Deiner Frage leite ich ab, dass die CalcOutput Klasse unnötig ist. ^_^ Als Asperger Authist weiß ich allerdigns nicht, ob diese Frage nur ein Hinweis, eine rethorische Frage oder eine Frage aus Interesse ist. Aufgrund meiner Beschaffenheit beantworte ich alle Fragen grundsätzlich, auch wenn sie nur rethorischer Natur sind:
Die CalcOutput Klasse habe ich geschrieben, weil die Ausgabe von Eingabe und Verarbeitung getrennt sein soll(te). Aber natürlich, der Code kommt auch ohne eine eigene Output-Klasse aus...
Ganz am Anfang meiner Reise hier in dem Forum, als die Taschenrechneraufgabe aus der Taufe gehoben wurde, hieß es als Vorgabe: Eingabe, Verarbeitung und Ausgabe sollen in jeweils einer Klasse erfolgen. Das habe ich jetzt umgesetzt. Oder versucht umzusetzen :gruebel:

Zitat:
Und wenn Du direkt mit den Prinzipien starten willst, dann ließ dich mal in das Single-responsibility principle ein.
Es gibt viele teils sehr verschiedene Erklärungen (nur eine Aufgabe, ein Grund sich zu ändern, eine Verantwortung, etc.), sie alle meinen aber grob das gleiche.
Das betreffend gibt's bei dir mehrere Probleme, ob sie für einen blutigen Anfänger relevant sind - naja, kann man drüber streiten, ich halte das SRP für so essenziell, zu früh geht nicht ^^

Klar, "Regeln und Konventionen" lernen sollte ich defintiv so früh wie möglich. Je länger ich das falsch mache, desto schwerer ist es nacher, sich umzugewöhnen. Daher können wir mit "Clean Code Konventionen" gerne auch jetzt schon starten.
Ich weiß nicht mehr, ob Du es warst, oder Ralf Jansen, aber einer von euch beiden hat mir ja schon mal das Buch "Clean Code" empfohlen und mir einen Link von David Tielke auf Youtube über das Thema geschickt.
Im Grunde habe ich auch versucht, das Ganze SRP mäßig zu schreiben. Werde mich damit aber noch mal eingehnder befassen.

Zitat:
Was würde der Fremde denken, tut die UserInputs-Methode?
Ich würde nicht auf die Idee kommen, dass diese Methode auch noch berechnet

Hm, UserInputs() fragt den User nach Eingabe der Ziffern die berechnet werden sollen, speichert diese ab und ruft dann eine neue Methode auf (CalcChoice()). Wo berechnet sie denn etwas? :gruebel: Hier bräuchte ich noch mal eine Hilfestellung.

Zitat:
Ich persönlich würde sie aufteilen: Eine Methode, die einen Wert abfragt und per Parameter stellst Du ein, wie dieser Wert in der Konsole heißen soll.
Hier stehe ich ebenfalls auf dem Schlauch :hair:

Zitat:
Das ist dann auch direkt nützlich, wenn Du dafür sorgen willst, dass dein Programm nicht abstürzt, wenn der Nutzer "a" statt "1" eintippt, das musst Du dann nur einmal machen und nicht zwei Mal.

Oh ja, das hab ich ganz vergessen. Danke, werde ich ändern, sodass kein Crash an dieser Stelle mehr vorkommt. Nur das mit dem "Aufteilen" (das quote zuvor), hab ich nicht so ganz verstanden.

Zitat:
Und die CalcChoice-Methode: Wie soll sie denn berechnen und was passiert damit? Das verrät der Name nicht.
Richtig heißen müsste sie also "AskForChoiceAndCalcResultAndPrintToConsole", was direkt sehr viel schlechter zu lesen ist und - spoiler - auch nicht der optimale Weg.
Da versteckt sich auch direkt ein sehr oft passender Indiz für verletztes SRP: "And" im Namen, zu lange Namen oder generell Schwierigkeiten, einen einfach verständlichen Namen zu finden.
Ah, der zweite Absatz ist ja interessant. :think: CalcChoice() hat mir selber auch schon nicht gefallen. Dasselbe gilt für UserChoiceLoop().


Ralf Jansen - Mo 06.12.21 14:30

Zitat:
Hm, UserInputs() fragt den User nach Eingabe der Ziffern die berechnet werden sollen, speichert diese ab und ruft dann eine neue Methode auf (CalcChoice()). Wo berechnet sie denn etwas?

Sie ruft die entsprechende Methode direkt auf daher kann man das so bezeichnen das sie auch berechnet. Eine echte (wünschenswerte) Trennung der Aspekte (Eingabe und Berechnung) bekommst du hin wenn du eine dritte Methode benutzt. Also eine Methode die die anderen beiden Methoden für Input und Berechnung nacheinander aufrufst. Also den Rückgabewert von Input über diese dritte Methode als Parameter an die Methode zum Berechnen weiterreichst. Dann sind Eingabe und Berechnung wirklich unabhängig voneinander was sie bei dir gerade nicht sind.

Wir hatten schonmal über den entsprechenden Pseudocode gesprochen. Hier nochmal mit den von dir benutzten Bezeichnern.


C#-Quelltext
1:
2:
3:
var userInputs = CalcInput.UserInputs();
var result = CalcInput.CalcChoice(userInputs);
CalcOutput.OutputValue(result);


Th69 - Mo 06.12.21 14:54

Dann sollte die Berechnugnsmethode aber besser gleich in Calculation rein.

Für Ein- und Ausgabe reicht es aber, wenn diese in einer eigenen Klasse (bzw. Methoden direkt der Hauptklasse RunProgram [wenn auch der Name nicht so toll ist]) untergebracht sind.


Palladin007 - Mo 06.12.21 15:01

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Die CalcOutput Klasse habe ich geschrieben, weil die Ausgabe von Eingabe und Verarbeitung getrennt sein soll(te). Aber natürlich, der Code kommt auch ohne eine eigene Output-Klasse aus...

Der Sinn ist korrekt, aber die Umsetzung?
Schau dir den restlichen Code an, ist da irgendetwas getrennt?
Nö, ist es nicht und für die eine Zeile ist diese Klasse schlicht unnötig.
Wenn Du die UI trennst, dann richtig und überall, sonst bringt das Ganze nichts.

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Im Grunde habe ich auch versucht, das Ganze SRP mäßig zu schreiben. Werde mich damit aber noch mal eingehnder befassen.

Das ist das Problem bei diesen sehr abstrakten Prinzipien: Nur weil Du glaubst, es verstanden zu haben, ist die Lücke zum echten Verständnis oft noch sehr groß.
Es gibt noch das Buch "Clean Architecture" von Robert C. Margin, das Buch ist mMn. sehr gut, allerdings sage ich das mit meinem Vorwissen. Wie gut das ein Anfänger versteht, kann ich nicht sagen.
Tatsächlich hat seine Erklärung zum SRP bei mir noch eine letzte Schuppe von den Augen gelöst, nämlich was mit "Verantwortung" wirklich gemeint ist und das ist nicht immer (oft, aber nicht immer) deckungsgleich mit "Aufgabe".
Aber wie gesagt: Es kann sein, dass Du damit deine Probleme haben wirst.

Bis dahin reicht es, wenn Du versuchst, die "Tätigkeiten" einer Methode zu erkennen - ausgehend vom Code nur dieser einen Methode.
Bei der "UserInputs"-Methode sind es drei Tätigkeiten: Zahl 1 erfragen, Zahl 2 erfragen, Berechnen. Natürlich kann man das alles weiter aufbrechen oder zusammenfassen, wie man sich die Tätigkeiten zurecht
Wenn Du das hast, kannst Du dir überlegen, wie Du weiter machst und in diesem Fall bietet es sich eben an, das Erfragen von Zahlen auszulagern.

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Hm, UserInputs() fragt den User nach Eingabe der Ziffern die berechnet werden sollen, speichert diese ab und ruft dann eine neue Methode auf (CalcChoice()). Wo berechnet sie denn etwas? :gruebel: Hier bräuchte ich noch mal eine Hilfestellung.

Wie Ralf schon schreibt:
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Sie ruft die entsprechende Methode direkt auf daher kann man das so bezeichnen das sie auch berechnet.

Ich versuche es nochmal aus einem anderen Blickwinkel:
Wenn das Programm die Methode ausgeführt hat - was wurde in diesem einen Schritt (die Methode ausführen) alles gemacht?
Wenn jemand Anderes den Code ließt und "UserInputs" ließt, dürfte der ziemlich überrascht sein, wenn plötzlich auch noch nach einer Berechnungsart gefragt und berechnet wird. Und genau diese "Hä?"-Momente willst Du vermeiden.

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Hier stehe ich ebenfalls auf dem Schlauch


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
double AskForInputNumber(int numberId)
{
    // Text anzeigen
    // Eingabe erfragen
    // Eingabe parsen
    // Ergebnis zurück geben
}


user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Ah, der zweite Absatz ist ja interessant. :think: CalcChoice() hat mir selber auch schon nicht gefallen. Dasselbe gilt für UserChoiceLoop().

Prinzipiell gilt das auch für UserChoiceLoop, ja. Allerdings kann man das nicht ewig so weiter treiben, irgendwann hast Du eine Methode, die mehrere andere aufruft und dann hast Du ein Problem.
Gedanklich musst Du dir das wie einen Baum vorstellen: Eine Komponente (Methoden, Klassen, Projekte, etc.) benutzt immer mehrere andere Komponenten.
Wenn Du dir nun diesen Baum aufbaust, hast Du am Ende die tatsächliche Logik, davor etwas andere Logik, die die darunter liegende Logik aufruft und so weiter.
Bis zum Anfang wird es immer abstrakter, weil sich hinter einem Methodenaufruf immer mehr verbirgt, bis ganz nach oben: Die Main-Methode ist die Wurzel des Baums, die ruft alles andere auf.
Die Benennung sollte ungefähr ähnlich ticken, auf oberster Ebene kannst Du nicht jedes Detail von ganz unten betrachten, aber Du kannst den Komponenten in der tieferen Schicht Namen geben und die dann als Einheit betrachten.

Beispiel:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Program.Main()
    Application.Run()
        Loop
            AskForInput()
            CalcChoice()
                AskForChoice()
                Calculate()
            OutputValue()
        AskWantToContinue()


Das soll nicht akkurat sein, sondern nur ein Beispiel für die Hierarchie sein.


OlafSt - Mo 06.12.21 23:34

Der Pseudocode von @Palladin007 ist doch ein Paradebeispiel für das S von SOLID.

Jeder Methodenname in dieser Abstraktion sagt genau eine Operation an. AskForInput fragt nach Eingaben (welche, sieht man nicht auf Anhieb). CalcChoice fragt nach den Operatoren. Calculate berechnet irgendwas. OutputValue spuckt n Wert aus. AskWantToContinue... You got it.

Ohne auch nur eine Zeile Code gesehen zu haben, kann man nur anhand der Methodennamen bereits schließen, das hier womöglich ein Taschenrechner programmiert wurde.

Jede dieser Methoden macht dann genau das, was der Name aussagt. Deine bisherigen Methoden, @Oldcat, haben oftmals mehr als eine Aufgabe: Abfragen des Userinpputs und Berechnen, oder Berechnen und Ausgabe. Das und muss da weg ;)


OldCat - Di 07.12.21 13:43

Hey ihr lieben :beer:

Ein paar Fragen habe ich immer noch :oops: , die mir beim Programmieren der neuen "Version" des Mini-Calculators den Gedankenfluss blockieren:

Problem 1.1:
Es wurde geschrieben, dass eine Methode nur eine Aufgabe erfüllen soll. Klingt einfach. Eine Methode erfüllt aber schon mehr als eine Aufgabe, wenn sie eine andere Methode aufruft (die dann logischerweise eine andere Aufgabe erfüllt), richtig?

So bei meinem bisherigen Code (Das ist nicht meine neuer Code), namentlich die UserInputs() Methode:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
        private static void UserInputs()
        {
            System.Console.WriteLine("Please enter your first digit: ");
            double digit1 = double.Parse(System.Console.ReadLine());
            System.Console.WriteLine($"You chose: {digit1}");

            System.Console.WriteLine("Please enter your second digit: ");
            double digit2 = double.Parse(System.Console.ReadLine());
            System.Console.WriteLine($"You chose: {digit2}\n");

            CalcChoice(digit1, digit2);
        }



Problem 1.2:
Das Schreiben einer Methode, dass Methoden aufruft, würde das Problem umgehen. Wenn ich das richtig verstanden habe, sollte ich aber niemals Methoden schreiben, die nur dazu dienen Methoden aufzurufen, oder? Wenn dem so ist, beißt sich meine innere Katze in den Schwanz/ich drehe mich gedanklich im Kreis und komme nicht weiter.


Problem 2:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Program.Main()
    Application.Run()
        Loop
            AskForInput()
            CalcChoice()
                AskForChoice()
                Calculate()
            OutputValue()
        AskWantToContinue()

Bin mir ja immer nicht sicher, ob ich den wirklich so lese, wie ich es lesen sollte. Daher ein paar Fragen:

Ich habe noch mehr Fragen dazu, aber diese könnten sich möglicherweise durch die Antworten auf die jetzt gestellten Fragen erübrigen.


Ralf Jansen - Di 07.12.21 14:43

Zitat:
Problem 1.1:
Es wurde geschrieben, dass eine Methode nur eine Aufgabe erfüllen soll. Klingt einfach. Eine Methode erfüllt aber schon mehr als eine Aufgabe, wenn sie eine andere Methode aufruft (die dann logischerweise eine andere Aufgabe erfüllt), richtig?


Es ist ein kleiner Unterschied was man anstreben sollte und was tatsächlich erreichbar ist ;) Der Satz "eine Methode macht genau eine Sache" ist genau sowas. Man sollte es anstreben auch wenn es nie wirklich erreichbar ist außer die gesamte Anwendung tut selbst auch nur eine Sache. Ziel der Trennung ist halt auch Dinge voneinander so zu trennen das sie austauschbar werden und das wird umso schwerer umso mehr eine Methode tut. Du kannst es möglicherweise ein wenig wie Lego betrachten. Es gibt Methoden die genau ein Legosteinchen darstellen aber um irgendwas zu erreichen muß man aber mehrerer zusammenstecken. Die einzelnen Legosteinchen sind dann aber weiterhin elementar und weitestgehend belibieg austauschbar ohne das das zusammengebaute auseinanderfällt. Das Ding Dinge austauschen zu können ohne das andere betroffen sind ist ein der zentralen Ziele solcher Konstrukte.

Irgendwer hat hier das EVA Prinzip erwähnt oder eingeführt. Das kann in der Größenordnung deiner Anwendung der Masstab sein. Mindest diese 3 Teile sollten scharf voneinander getrennt sein (einzelne Legosteine) und nur von Methoden zusammengeführt werden so das du zumindest theoretisch die 3 Einzelteile austauschen kannst ohne das die anderen Beteiligten irgendwie angefasst oder geändert werden müßten.

Zitat:
Die Methode Run() in der Application class startet den Rest der Logik, welche auch vollständig in der Application class geschrieben wird?


Die Zeile kannst du erstmal ignorieren. "Application.Run()" hilft gerade nicht du kannst die Loop auch direkt aus der Main der Programm Klasse starten.


OldCat - Mi 08.12.21 20:06

Momentan stecke ich sowas von fest. :eyecrazy:

Es ist das erste Mal, dass ich mich gezwungen sehe, (wenn ich die Vorgaben richtig interpretiere), Übergabeparameter an die Main() Methode zu übergeben (double digit1, double digit2). Das lässt der Compiler nicht zu.

Ich folge den Vorgaben:



Folgende Methoden habe ich nochmal umbenannt aus dem Beispiel: AskForInput() = AskForDigits(); AskForChoice() = AskForOperator(); Die Calculate() Methode aus dem Beispiel sind die vier Methoden der vier Grundrechenarten, wie gehabt.

Mein Code funktioniert nicht, so sieht er aus aktuell aus:

Der Code besteht aus zwei Klassen: Program.Main() und Application.Run(). In Main() erstelle ich das Objekt myCalc und rufe den Member Run() auf.

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
namespace MiniCalculator
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Application myCalc = new Application();
            myCalc.Run();
        }
    }
}

In Application.Run() soll die do while() Schleife starten, aktuell noch auskommentiert. Innerhalb der Schleife wird private void AskForDigits() aufgerufen, (diese fragt nur noch nach den Operanden und speichert diese ab.)
Die Methode CalcChoice() soll die AskForOperator() Methode aufrufen. In ihr werden über die switch case Anweisung weiterhin die Methoden der vier Grundrechenarten aufgerufen und deren Rückgabewerte in result gespeichert. (Hier fange ich an, Bauchschmerzen zu bekommen.) AskForOperator braucht die Übergabeparameter (double, double) -> wird von CalcChoice(couble, double) aufgerufen, die innerhalb der do while() Schleife von Run() aufgerufen wird. Dafür braucht Run() ebenfalls die Übergabeparameter (double, double) -> Und dafür braucht dann die Main() Methode die Parameter (double, double)...Und hier sind meine Bauchschmerzen dann ein konkreter Fehler. Der Compiler erkennt keinen Einsteigspunkt mehr:

Application.cs:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
namespace MiniCalculator
{
    internal class Application
    {
        public void Run(double digit1, double digit2)
        {
            //do
            //{
            AskForDigits();
            CalcChoice(digit1, digit2);
            //}while ();
            System.Console.WriteLine("Press any key to exit...");
            System.Console.ReadKey();
        }

        private void AskForDigits()
        {
            System.Console.WriteLine("Please type in your first digit.");
            double digit1 = int.Parse(System.Console.ReadLine());

            System.Console.WriteLine("Please type in your second digit.");
            double digit2 = int.Parse(System.Console.ReadLine());
        }

        private void CalcChoice(double digit1, double digit2)
        {
            AskForOperator(digit1, digit2);
        }

        private void AskForOperator(double digit1, double digit2)
        {
            System.Console.WriteLine(@"Please choose an operator by number and hit enter:
> 1.) Addition
> 2.) Subtraction
> 3.) Multiplication
> 4.) Division"
);

            int userChoice = int.Parse(System.Console.ReadLine());
            double result;

            switch (userChoice)
            {
                case 1:
                    result = CalculateAddition(digit1, digit2);
                    break;
            }
        }

        private double CalculateAddition(double a, double b)
        {
            return a + b;
        }

        private double CalculateSubtraction(double a, double b)
        {
            return a - b;
        }

        private double CalculateMultiplication(double a, double b)
        {
            return a * b;
        }

        private double CalculateDivision(double a, double b)
        {
            return a / b;
        }
    }
}


Program.cs:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
namespace MiniCalculator
{
    internal class Program
    {
        static void Main(double digit1, double digit2)
        {
            Application myCalc = new Application();
            myCalc.Run(digit1, digit2);
        }
    }
}


Fehler CS5001 Das Programm enthält keine als Einstiegspunkt geeignete statische Main-Methode.
Warnung CS0028 "Program.Main(double, double) hat ein falsche Signatur, um ein Einstiegspunkt zu sein.


Ralf Jansen - Mi 08.12.21 20:19

Zitat:
Fehler CS5001 Das Programm enthält keine als Einstiegspunkt geeignete statische Main-Methode.


Die main hatte entweder keine Parameter oder einen string[] Parameter (üblicherweise namens args). Alles andere ist keine gültige Signatur. Wie man Parameter an eine Konsolen Anwendung übergibt findest du sicher in der Dokumentation zur main Methode.

Aber warum willst du das? Du möchtest doch nach den Operatoren fragen und sie nicht schon an dein Programm übergeben.
Also entweder als Kommandozeilenparameter an das Programm übergeben (dann über besagten args Parameter der main Methode) oder danach fragen aber doch nicht beides :nixweiss:

AskForDigit schreibt die Eingaben in lokale Variablen die sind nach dem Methodenaufruf vergessen. Du musst die aus der Methode rausgeben (wie oft hatten wir das jetzt schon das du die Dinge auch wieder aus den Methoden rausholen musst?). Nur weil Variablen gleich heißen sind das nicht die gleichen Variablen. digit1, digit2 haben in Application-Run nichts mit den gleichnamigen Variablen in AskForDigits zu tun.


OlafSt - Do 09.12.21 00:15

Application und .Run() gibt es nur in GUI-Programmen (WinForms, WPF). Du aber hast eine Konsolenanwendung - da gibts sowas nicht.

Die statische Main-Methode ist bereits der Einsprungpunkt - also quasi dein .Run() ;) Die Main-Methode ist somit der zentrale Punkt, von wo aus alles gesteuert wird. Und von hier aus erzeugst du deine Klasse und rufst du auch fleißig deine Methoden eben dieser Klasse auf.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
namespace MiniCalculator
{
    internal class Program
    {
        static void Main()
        {
            MyCalculationClass myCalc = new MyCalculationClass();
            //Abfrage der Eingabewerte hier
            myCalc.AskValues();  //Ich sage jetzt schon, das das NICHT funktionieren wird
            myCalc.AskOperator();
            //usw. usf.
        }
    }
}


Siehst du, wie das funktionieren soll mit der Main-Methode ?

Manchmal wäre ein Pair-Programming gar nicht schlecht...


Palladin007 - Do 09.12.21 01:19

Zitat:
Application und .Run() gibt es nur in GUI-Programmen (WinForms, WPF). Du aber hast eine Konsolenanwendung - da gibts sowas nicht.

Gibt's schon - wenn man sowas braucht, aber das ist hier definitiv nicht der Fall.
Deshalb war mein Baum-Beispiel auch nur ein Beispiel um zu zeigen, was ich mit den Namen meine.

Die Schleife würde ich auch einfach in die Main-Methode legen.


@OlafSt, zum Beispiel:
Ich persönlich bin kein Freund von solchem Vorgehen - zumindest so, wie es im Code aussieht.
Aber bevor ich kritisiere: Meinst Du, dass die Klasse die Ergebnisse der aufgerufenen Methoden (eingegebene Werte, Operator, etc.) als lokalen Zustand behält und in den späteren Methoden (Calculate) benutzt?
Wenn nicht, dann bin ich still :D


OlafSt - Do 09.12.21 08:25

Pscht ;)

Ich will sehen, ob unser angehender Meister-Programmierer mitdenkt :D


OldCat - Do 09.12.21 10:26

Zitat:
Ich will sehen, ob unser angehender Meister-Programmierer mitdenkt :D


Oh... bitte macht euch nicht über mich lustig. Ich komme mir eh schon total minderbemittelt vor und wundere mich, dass ihr überhaupt noch auf meine Probleme eingeht...


Th69 - Do 09.12.21 11:51

Da du sonst bis Weihnachten nicht vorankommst, habe ich jetzt mal das Programm für dich erstellt (auf Grundlage deines bisherigen Codes):

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
using System;

// Program.cs
namespace Calculator
{
    public class Program
    {
        static void Main(string[] args)
        {
            Application app = new Application();
            app.Run();
        }
    }
}

// Application.cs
namespace Calculator
{
    public class Application
    {
        private const string AppTitle = "Mini Calculator";
        
        public void Run()
        {
            Console.Title = AppTitle;

            Console.WriteLine("###############");
            Console.WriteLine(AppTitle);
            Console.WriteLine("###############\n");

            string s;
            
            do
            {
                var userInputs = Input();
                var result = Calculate(userInputs);
                OutputResult(result);

                Console.WriteLine("Do you like to solve another arithmetical problem? (y/n)");
                s = Console.ReadLine().Trim().ToLower();
            } while (s.StartsWith("y"));

            Console.WriteLine("\nPress any key to exit...");
            Console.ReadKey();
        }
        
        public class UserInputs
        {
             public double Number1 { get; set; }
             public double Number2 { get; set; }
             public int Operator { get; set; }
        }

        public static UserInputs Input()
        {
            Console.WriteLine("Please enter your first number: ");
            double number1 = double.Parse(Console.ReadLine());
            Console.WriteLine($"You chose: {number1}");

            Console.WriteLine("Please enter your second number: ");
            double number2 = double.Parse(Console.ReadLine());
            Console.WriteLine($"You chose: {number2}");

            Console.WriteLine("Choose your operator by number and hit enter:");
            Console.WriteLine("> 1.) Addition");
            Console.WriteLine("> 2.) Subtraction");
            Console.WriteLine("> 3.) Multiplication");
            Console.WriteLine("> 4.) Division");

            int op = 0;
                
            while (true)
            {
                op = int.Parse(Console.ReadLine());
                Console.WriteLine($"You chose: {op}");

                if (op < 1 || op > 4)
                {
                    Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
                    continue;
                }
                    
                break;
            }
                
            return new UserInputs() { Number1 = number1, Number2 = number2, Operator = op };
        }
        
        public double Calculate(UserInputs userInputs)
        {
            double result = 0;
          
            switch (userInputs.Operator)
            {
                case 1:
                    result = Calculation.Addition(userInputs.Number1, userInputs.Number2);
                    break;
                case 2:
                    result = Calculation.Subtraction(userInputs.Number1, userInputs.Number2);
                    break;
                case 3:
                    result = Calculation.Multiplication(userInputs.Number1, userInputs.Number2);
                    break;
                case 4:
                    result = Calculation.Division(userInputs.Number1, userInputs.Number2);
                    break;
            }
            
            return result;
        }
        
        public void OutputResult(double result)
        {
            Console.WriteLine($"Result: {result}");
        }
    }


// Calculation.cs
namespace Calculator
{
    public static class Calculation
    {
        public static double Addition(double x, double y)
        {
            return x + y;
        }

        public static double Subtraction(double x, double y)
        {
            return x - y;
        }

        public static double Multiplication(double x, double y)
        {
            return x * y;
        }

        public static double Division(double x, double y)
        {
            return x / y;
        }
    }
}

Ich habe alle Namen in englisch gehalten, die explizite Angabe des Namensbereiches System jedoch bei den Aufrufen entfernt.

Hier noch als lauffähiger Code: Ideone-Code: Calculator [https://www.ideone.com/SDzX1N] (mit vorgegebenem User Input)

Der Code ist (aus professioneller Sicht) noch verbesserungsfähig, aber bitte versuche ihn zu verstehen und wenn nicht dann stelle explizite Fragen zu einzelnen Codeteilen.


OldCat - Do 09.12.21 11:54

Zitat:
Die main hatte entweder keine Parameter oder einen string[] Parameter (üblicherweise namens args). Alles andere ist keine gültige Signatur. Wie man Parameter an eine Konsolen Anwendung übergibt findest du sicher in der Dokumentation zur main Methode.

Aber warum willst du das?

Laut Microsoft.docs sind keine beliebigen Parameter in Main() erlaubt, außer Main(string[] args):

Zitat:
The following list shows valid Main signatures:

public static void Main() { }
public static int Main() { }
public static void Main(string[] args) { }
public static int Main(string[] args) { }
public static async Task Main() { }
public static async Task<int> Main() { }
public static async Task Main(string[] args) { }
public static async Task<int> Main(string[] args) { }


Auch als Anfänger leiste ich mir jetzt mal ne Meinung ^_^: Ich finde, in der Main() Methode einer Konsolenanwendung sollte so wenig Code wie möglich stehen. Alles in eine Main() Methode klatschen ist doch gegen S.O.L.I.D.? Dann brauche ich auch keine Rückgabewerte oder Ein-/Übergabeparameter mehr. Ich hab hier älteren Code, vor unserer "Zusammenarbeit". Auch einen Taschenrechner. Der Code ist Müll. Er funktioniert, ist aber Müll: Alles steht dort in der Main() Methode und alle Methoden sind void und besitzen keine Ein-/Übergabeparameter. Ist ja auch nicht nötig, da eh alles in einer Methode steht: Der Main() Methode eben...
Das die Main() Methode nicht irgendeine Methode ist @OlafSt, sondern DER Einstiegspunkt für das jeweilige Programm, das ist einer der ersten Dinge, die ich lernte. Für mich ist Main() "untouchable". Hier will ich schon gar nicht irgendwelche Parameter einfügen. Was ja auch gar nicht erlaubt ist, laut Microsoft.docs, (siehe oben).

Zitat:
user profile iconOlafSt Application und .Run() gibt es nur in GUI-Programmen (WinForms, WPF). Du aber hast eine Konsolenanwendung - da gibts sowas nicht.

Als Anfänger habe ich bisher eine Sache streng beherzigt: "Fange mit Konsolenanwendungen an und bleibe dort eine Weile, bevor Du Dich entscheidest, etwas anderes zu machen." Aus diesem Grund, habe ich mich bisher nicht mit WPF und Co. beschäftigt. Auch nicht, als ich mir das Buch "C# für Dummies" kaufte, und dort leider mit WinForms gestartet wird, statt mit Konsolenanwendungen. Es scheint mir ein gutes Buch zu sein. Doch liegt es jetzt hier auf meinem Bücherstapel auf meinem Tisch... bis mir mein Inneres ein "Go" für WPF und Co. gibt.

Möglicherweise sind dort die Begriffe "Application" und ".Run()" feste Begrifflichkeiten. In einer Konsolenanwendung sind sie es nicht. Hier kann ich eine Klasse "Application", oder eine Methode Run() nennen, ohne dabei einen syntaktischen Fehler zu machen, oder SOLID zu verletzen.

Zitat:
Manchmal wäre ein Pair-Programming gar nicht schlecht...

Für mich wäre es ein Traum mit jemandem von euch Pair-Programming zu machen. :dance2: Ich denke auch, dass eine ganze Menge an Verständnisproblemen dann auch gar nicht auftreten würden, wie das hier über das Chatten der Fall ist...

Ah, ich sehe gerade, Th69 hat mir was geschrieben.

Moderiert von user profile iconTh69: C#- durch Quote-Tags ersetzt


OldCat - Do 09.12.21 11:55

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Da du sonst bis Weihnachten nicht vorankommst, habe ich jetzt mal das Programm für dich erstellt (auf Grundlage deines bisherigen Codes):
...


Danke!

Moderiert von user profile iconTh69: Zitat eingekürzt.


OlafSt - Fr 10.12.21 08:29

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Zitat:
Ich will sehen, ob unser angehender Meister-Programmierer mitdenkt :D


Oh... bitte macht euch nicht über mich lustig. Ich komme mir eh schon total minderbemittelt vor und wundere mich, dass ihr überhaupt noch auf meine Probleme eingeht...


Das haben wir auch nicht, ganz im Gegenteil. Ich habe meinen Code absichtlich mit Fehlern gespickt, die natürlich sofort aufgefallen sind. Doch sind sie auch Dir aufgefallen ?

Aber nun verdaue erst mal den Code von @Th69 :D


Th69 - Fr 10.12.21 09:17

Da ich gestern nicht so viel Zeit hatte (vor meiner 3. Impfung), ist mir gerade noch eingefallen, daß man die Operatorabfrage-Schleife besser so schreiben sollte:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
while (true)
{
    op = int.Parse(Console.ReadLine());
    Console.WriteLine($"You chose: {op}");

    if (op >= 1 && op <= 4)
        break;

    Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
}

(ist kürzer und einfacher zu lesen)

Und schon mal als Ausblick für OldCat, wie man den Code exception-sicher macht (damit das Programm nicht beendet wird, wenn der Anwender keine Zahl eingibt):

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
if (int.TryParse(Console.ReadLine(), out op))
{
  // ok: in op is the converted value
}
else
{
  // error: not an integer
}


OldCat - Fr 10.12.21 12:57

Zitat:
user profile iconOlafSt Das haben wir auch nicht, ganz im Gegenteil.

Das kaufe ich euch nicht ab *lach* :P

Zitat:
Ich habe meinen Code absichtlich mit Fehlern gespickt, die natürlich sofort aufgefallen sind.

Das ist gemein :wink: Aber jetzt verstehe ich, warum das alles so verwirrend ist. Ihr linkt mich :P , um zu sehen, wie ich darauf reagiere. Als Asperger muss ich erstmal auf so eine Idee kommen :D . Und ich wundere mich die ganze Zeit, warum die Vorschläge mich so verwirren... Für mich galt bis zu diesem Augenblick, das alles, was ihr mir hier anbietet richtig und unschlagbar ist...

Zitat:
Doch sind sie auch Dir aufgefallen ?

Nein, ich denke nicht ... :oops: ... Solltest Du das hier meinen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
namespace MiniCalculator
{
    internal class Program
    {
        static void Main()
        {
            MyCalculationClass myCalc = new MyCalculationClass();
            //Abfrage der Eingabewerte hier
            myCalc.AskValues();  //Ich sage jetzt schon, das das NICHT funktionieren wird
            myCalc.AskOperator();
            //usw. usf.
        }
    }
}

Dann hat es mich einfach verwirrt, und ich dachte mir: Nee, so werd ich das nicht schreiben 8)
Siehe auch mein Anfängerstatement zur Main() Methode in Konsolenapps.

Th69 hat folgendes geschrieben:
Und schon mal als Ausblick für OldCat, wie man den Code exception-sicher macht

Dazu werde ich mal schauen, wie ich es schreiben würde...

Moderiert von user profile iconTh69: C#- durch Quote-Tags ersetzt


Palladin007 - Fr 10.12.21 16:41

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Aber jetzt verstehe ich, warum das alles so verwirrend ist.

Haben wir nicht - und soweit ich das in Erinnerung habe, war das bisher der einzige "Test".
Außerdem stand im "Test"-Code ja auch, dass das so nicht funktioniert - typisch Pseudocode eben.

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Als Asperger muss ich erstmal auf so eine Idee kommen.

Du kannst das auch noch weitere 13 Mal wiederholen, aber es wird dir nicht helfen :P


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
namespace MiniCalculator
{
    internal class Program
    {
        static void Main()
        {
            MyCalculationClass myCalc = new MyCalculationClass();
            //Abfrage der Eingabewerte hier
            myCalc.AskValues();  //Ich sage jetzt schon, das das NICHT funktionieren wird
            myCalc.AskOperator();
            //usw. usf.
        }
    }
}

Schau dir den Code doch mal an und überlege, was er tut bzw. tun sollte.
Und die zwei Methoden - wie heißen sie, was sollten sie tun und was nicht?
Und dann versuch die mal zu implementieren, aber wichtig: Beachte das, was Du vorher festgestellt hast, was die Methode tun darf und was nicht.

Aber wie schon im Code steht:
Zitat:
Ich sage jetzt schon, das das NICHT funktionieren wird

Warum, das wirst Du schon herausfinden, wenn Du versuchst, sie zu implementieren.


OldCat - Fr 10.12.21 17:02

Zitat:
Schau dir den Code doch mal an und überlege, was er tut bzw. tun sollte.
Und die zwei Methoden - wie heißen sie, was sollten sie tun und was nicht?


Es wird in der Main() Methode ein Objekt names myCalc erstellt. Dieses Objekt ist eine Instanz der MyCalculationClass. Diese Klasse gibt es in meinem Code nicht. Daher kann sie auch nicht funktionieren. Die Bezeichnung "Class" ist darüber hinaus redundant. Die Klasse hätte genausogut auch MyCalculation heißen können.

Anschließend wird über das Objekt myCalc die Methode AskValues() aufgerufen. Diese Methode gibt es in meinem Code nicht. Also kann es nicht funktionieren. Danach wird über das Objekt myCalc die Methode AskOperator() aufgerufen. Diese Methode gibt es in meinem Code auch nicht, also kann auch sie nicht funktionieren.

Zu guter Letzt, selbst wenn ich diese Methoden hätte (ich könnte sie natürlich schreiben, bzw. meine Gegenstücke umschreiben) kann der Aufruf der Methoden nicht funktionieren, da es in der Main() Methode diese Methoden nicht gibt. Möglicherweise gibt es sie in der Klasse MyCalculationClass, aber dann müsste der Aufruf myCalc.MyCalculationClass.Methode xy() heißen.

Nur ... weiter helfen tut mr das alles nicht :nixweiss:

Es sei denn, ich bin zu blöd?? :nixweiss:


Palladin007 - Fr 10.12.21 18:19

Zitat:
Die Bezeichnung "Class" ist darüber hinaus redundant. Die Klasse hätte genausogut auch MyCalculation heißen können.

Würde ich so unterschreiben, aber darum ging's mir gar nicht.

Und dass es sie bei dir noch nicht gibt, ist klar.
Mir ging es darum, wie die Methoden heißen, was Du anhand der Namen erwarten würdest, was sie tun und wie man es implementiert.

Zitat:
ich könnte sie natürlich schreiben, bzw. meine Gegenstücke umschreiben

Ersteres (schreiben, nicht umschreiben) - ist bei sowas oft klüger, weil man sich dann nicht vom alten Code "ablenken" lässt und direkt etwas mehr Übung bekommt.

Zitat:
Möglicherweise gibt es sie in der Klasse MyCalculationClass, aber dann müsste der Aufruf myCalc.MyCalculationClass.Methode xy() heißen.

Nein, ich weiß auch nicht, wo Du das her hast.
Der Code von OlafSt ist schon korrekt so, zumindest technisch. Nur bei der Implementierung wird es schwierig, weil die Methoden, so wie sie da stehen, einfach nicht ausreichen.
Also versuch sie zu implementieren, ohne dass sie dabei mehr tun, als der Name vermuten lassen würde (das schließt auch darin aufgerufene Methoden ein).
Und das meine OlaftSt: Das wird dir nämlich nicht gelingen, nicht ohne die Methoden-Signatur und die Aufrufe anzupassen - wie Du es anpassen musst, das solltest Du herausfinden.

So langsam glaube ich aber, dass Du kein Verständnisproblem hast (bzw. nicht nur), sondern einfach die Grundlagen fehlen.
Es hat noch nie jemandem geholfen, einfach anzufangen und ohne Grundwissen von einem Problem zum Nächsten zu stolpern, bis am Ende irgendwas bei raus kommt.
So kann man zwar auch C# lernen, aber es dauert sehr, sehr viel länger und ist für alle Beteiligten sehr viel anstrengender - und dein Wissen ist am Ende immer noch lückenhaft.
Oder schätze ich die Situation falsch ein?

Es gibt gute Bücher, die alles Nötige erklären, wie z.B. "C# 8 mit Visual Studio 2019" von Andreas Kühnel - ich hab die alte Version von 2010 durchgearbeitet.
Du solltest auf jeden Fall die 2019er Variante nehmen, einfach weil sich seit 2016 wirklich sehr viel getan hat.


Ralf Jansen - Fr 10.12.21 18:57

Sorry. Ich hab den philosophischen Freitags Blues ;)

Zitat:
Es hat noch nie jemandem geholfen, einfach anzufangen und ohne Grundwissen von einem Problem zum Nächsten zu stolpern, bis am Ende irgendwas bei raus kommt.
So kann man zwar auch C# lernen, aber es dauert sehr, sehr viel länger und ist für alle Beteiligten sehr viel anstrengender - und dein Wissen ist am Ende immer noch lückenhaft.
Oder schätze ich die Situation falsch ein?


Ist vermutlich eine Typfrage bezüglich des Lernenden. Ich lerne so. Lernen ist für mich erfolgreich vorwärts scheitern ;)
An meinen Fehlern lerne ich mehr als an meinen Erfolgen. In einem Buch lerne ich erfolgreich Programme nachschreiben, den Erkenntnisgewinn dabei sehe ich aber eher mager. Ich muß wissen warum die anderen denkbaren Wege jenseits der gezeigten Lösung nicht funktionieren. Ich will später echte Probleme lösen keine "Buch" oder "Beispiel" Probleme und gegenüber neuen Problemen muß ich primär Wissen was nicht funktioniert um dann im Restbereich des Möglichen die Lösung zu suchen.

Oder ein anderes Bild. Ortskenntnis ist wenn man die anderen Wege, auch Sackgassen, kennt. Wenn man nur den einen richtigen Weg kennt und da ist doch mal eine Baustelle hat man verloren. Ist wohl ein Bild für ältere Menschen die noch Wissen wie sowas ohne Navi ging ;)

Oder noch ein anderes Bild (mit einem Bild) um ein guter Maler zu werden reicht es nicht sich einen van Goch anzusehen ich muß malen. Und wenn am anfang nur ein Strichmännchen rauskommt ja dann ist es halt nur ein Strichmännchen. Der Erkenntnisgewinn wie man malt ist aber immer noch besser als beim betrachten des van Goch.

Hier ist glaube ich eher das Problem das ein Forum da an seine Grenzen stößt. Das was wir sagen ist bestimmt oft hilfreich aber man muss das auch interpretieren können und dazu brauch man eigentlich bereits die Fähigkeiten die er ja gerade erst versucht zu erlangen und der Kommunikationsweg ist da einfach sehr sehr lang und der Groschen fällt dann halt später.

Das Beispielprojekt von TH69 sehe ich in der gleichen Ecke. Er sieht jetzt eine vermutlich gute richtige Lösung das Wissen warum die wie entstanden ist fehlt aber weiterhin.
Den Weg zu zeigen wie man von der Idee zur Lösung kommt und warum man wie beim schreiben abgebogen ist fehlt. Wir können hier nur regelmäßig Feedback geben und so versuchen zu lenken bis der Groschen fällt. Ähnlich, aber schwieriger, als als Tutor daneben zu sitzen. Führt hoffentlich dann aber dazu das ein anderes ähnliches Problem leichter gelöst werden kann. Weil das gewohne Wissen wie man zur Lösung kommt dann auch anwendbar ist. Und nicht weil im nächsten Problem aus Taschenrechner jetzt vielleicht ein Einheitenconverter wird das Schulterzucken ausbricht.


Palladin007 - Fr 10.12.21 19:31

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Ist vermutlich eine Typfrage bezüglich des Lernenden.

Ne, eher eine Frage, worauf man es bezieht ^^

Ich meine nicht die Frage, wie man etwas lösen kann und ob man das auch sollte, sondern die blanken Grundlagen, wo es kein "könnte", "sollte" oder "würde" gibt.
Du kannst z.B. keine OOP verstehen und was sie bringt, wenn Du nicht weißt, wie man Klassen und Methoden benutzt.
Klar, es geht auch ohne dieses Grundwissen, aber damit stellst Du dir halt selber ein Bein. Hier sollte man mMn. vorarbeiten, um es danach deutlich leichter zu haben.

Bei komplexeren Themen (z.B. die OOP selbst) gebe ich dir aber Recht: Das nach dem Lehrbuch zu lernen, liegt nicht jedem.
Ich bevorzuge meist eine Mischvariante: Nachlesen, wie man es machen "sollte" und dann selber experimentieren und dabei ggf. scheitern.
Es kommt immer wieder vor, dass ich etwas versuche und einfach nicht begreife, warum es nicht geht - bis ich dann feststelle, in der Doku steht ja, dass genau das nicht geht :D Hätte ich vorher in die Doku geschaut, hätte ich mir sehr viel Zeit und Nerven sparen können.

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Hier ist glaube ich eher das Problem das ein Forum da an seine Grenzen stößt. Das was wir sagen ist bestimmt oft hilfreich aber man muss das auch interpretieren können und dazu brauch man eigentlich bereits die Fähigkeiten die er ja gerade erst versucht zu erlangen und der Kommunikationsweg ist da einfach sehr sehr lang und der Groschen fällt dann halt später.

Das drückt es vermutlich besser aus, als mein Kommentar dazu - auch wenn die Aussage nicht exakt gleich ist.


OldCat - Fr 10.12.21 20:50

Zitat:
Möglicherweise gibt es sie in der Klasse MyCalculationClass, aber dann müsste der Aufruf myCalc.MyCalculationClass.Methode xy() heißen.

Huch ja, ein Denkfehler ... :lol:

Zum Thema, wie wir in einem Selbststudium am Besten Programmieren lernen, ist reines Nachprogrammieren definitiv nicht ausreichend. Denn genau das habe ich bisher gemacht: Bücher lesen und Nachprogrammieren. Von Anfang an wollte ich Aufgaben haben. Mein erstes Buch, mit dem meine Reise begann war (und ist) das Buch C# Programmieren für Einsteiger, Michael Bonacina [ https://www.amazon.de/Programmieren-Einsteiger-leichte-Experten-Einfach-ebook/dp/B07P2MM6GT/ref=sr_1_2?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=3RPIBXK0KVFQW&keywords=einstieg+in+c%23+mit+visual+studio+2019&qid=1639158057&sprefix=Eins%2Caps%2C193&sr=8-2]. Gut ist: Es gibt einem ein paar Mini-Aufgaben am Ende eines jeden Kapitels. Somit programmiert man sogar nicht nur nach. Sondern man kann schauen, ob man auch verstanden hat, was man zuvor nachprogrammiert hat. Das Buch hat aber deutliche Schwächen: CamelCase, PascalCase, und co. werden nicht erwähnt. Auch Zeichenfolgeninterpolation oder ausführliche Bezeichner werden nicht erwähnt.

Noch während ich mich durch die ersten Kapitel hangelte, entdeckte ich Microsoft.learn für mich. Es ist ähnlich aufgebaut: Es wird einem via Modul ein Thema erklärt, am Ende muss man Aufgaben meistern, die die Module zum Thema haben. Die Module sind Teil von Lernpfaden. Komplett durchgearbeitet habe ich den Lernpfad "Erste Schritte". Weitere Lernpfade habe ich angefangen (Hinzufügen von Logik zu Ihren Anwendungen mit C#, Arbeiten mit Daten in C#, Erstellen von .NET-Anwendungen mit C#). Microsoft.learn kann ich als Einstieg in die Materie wirklich empfehlen. Hier lernte ich ne Menge über die Syntax, Escapezeichensequenzen, Literale versus Variablen, Zeichenfolgeninterpolation, Ausführliche Bezeichner, etc.
Während ich mich durch den ersten Lernpfad lernte, kaufte ich mehr Bücher: Einstieg in C# mit Visual Studio 2019: Ideal für Programmieranfänger [https://www.amazon.de/Einstieg-Visual-Studio-2019-Programmieranf%C3%A4nger/dp/3836270447/ref=sr_1_1?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&keywords=einstieg+in+c%23+mit+visual+studio+2019&qid=1639160566&sr=8-1], und Visual C# 2019 – Grundlagen, Profiwissen und Rezepte [https://www.amazon.de/Visual-2019-Grundlagen-Profiwissen-Book/dp/3446458026/ref=pd_sbs_8/262-6442095-8815755?pd_rd_w=ymWZU&pf_rd_p=b2a25e73-6e6a-41bb-a7ce-5f2d2342b819&pf_rd_r=CVJNV7X0R2GR0P1Z07NJ&pd_rd_r=09eeaafd-3532-4e87-b2d8-e5f005dfb84d&pd_rd_wg=7yLD4&pd_rd_i=3446458026&psc=1]. Diese Bücher habe ich mal kurz überflogen, schaue manchmal was nach. Aber "Durcharbeiten" fand hier nicht statt.

Dann entdeckte ich auf Youtube Michael Hadley von der Columbia University of Art, der dort Playlists zu Verfügung stellt. Ich begann seine Playlist "Introduction to Programming in C#". Dort habe ich ca. die Hälfte angesehen und nachgearbeitet. Dazu habe ich mir dann noch das Buch UML 2.5: Das umfassende Handbuch. [https://www.amazon.de/UML-2-5-umfassende-Handbuch-Diagrammtypen/dp/3836260182/ref=sr_1_2?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&keywords=UML&qid=1639161102&s=books&sr=1-2] besorgt. Sodass ich wenigstens grundlegende Klassendiagramme schreiben kann...

Und in diesem Prozess kam ich zu euch.

Ich weiß nicht, was ihr im Kopf habt, wenn ihr von Grundlagen sprecht. Die nicht-OOP Basics von "Was sind Datentypen", "Was ist der Unterschied zwischen Literale und Variablen", "Was sind Namespaces, Klassen und Methoden", "If elseif else", "Switch case", And OR Operatoren, do while, while, for und foreach Schleifen, etc. sind mir bekannt, bzw. kann/könnte ich nutzen.
OOP Paradigma scheint mir ganz offensichtlich Schwierigkeiten zu bereiten.

Was ich allerdings neben all dem sagen wollte, ist:

Hier, durch euch, habe ich eine x mal steilere Lernkurve hingelegt, als die Prozesse zuvor! :flehan: Daher ist mir hier bewusst gewodern, wie wichtig es ist, nicht einfach für sich als Quereinsteiger mittleren Alters (41) ein Selbstudium ganz alleine zu machen. Man lernt wirklich nur halb so viel :wink:

PS: Das Buch "C# 8 mit Visual Studio 2019" werde ich mir besorgen ^_^ Leider wird das erst im Februar was, bis dahin ist kein Budget frei.

Aktuell versuche ich den Code von Th69 zu verstehen. Habe ihn ausgedruckt, vergleiche ihn Zeile für Zeile mit meinem, und schreibe das ganze in eine Texdatei.


OldCat - Sa 11.12.21 16:03

Zitat:
user profile iconPalladin007 So langsam glaube ich aber, dass Du kein Verständnisproblem hast (bzw. nicht nur), sondern einfach die Grundlagen fehlen.

Das ist sehr gut möglich! Habe ja in etwa angegeben, wo ich stehe.

Könntest Du vielleicht mir hier mal in Kurzform auflisten, was alles zu den Grundlagen gehört? Nur, damit wir dieses Problem aus der Welt schaffen können. Dann wäre die Kommunikation schon mal besser, wenn ich die Grundlagen erlernt habe.


Th69 - Sa 11.12.21 16:59

Vllt. fehlt dir auch eher ein Grundverständnis der OOP? Du könntest dazu mal das OpenBook Objektorientierte Programmierung [https://openbook.rheinwerk-verlag.de/oop/] anschauen.


Palladin007 - Sa 11.12.21 18:56

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Könntest Du vielleicht mir hier mal in Kurzform auflisten, was alles zu den Grundlagen gehört?

Kann ich nicht - zum Einen würde es den Rahmen sprengen, zum Anderen würde ich garantiert etwas vergessen.

Dafür gibt es ja Bücher und das ist auch einer der großen Vorteile.
Von "Visual C# 2012" weiß ich, dass die ersten Kapitel eine gute Idee sind.
OOP wird auch erklärt, aber um beurteilen zu können, wie gut man das versteht, ist das mir schon zu sehr ins Fleisch übergegangen :D
Wenn Du die alte (dafür kostenlose) Version liest, musst Du bloß darauf achten, dass Angaben zum Framework oder VisualStudio nicht mehr aktuell sind - die C#-Grundlagen sind aber unverändert.
Und ja, es ist ein Buch - man sollte es auch nicht als "wie man etwas tun sollte" verstehen, sondern als "wie man etwas tun kann" - es erklärt die Syntax und wie man sie benutzt.

Das von Th69 verlinkte OOP-Buch kenne ich nicht, aber meiner Erfahrung nach ist beim Thema OOP besser, je mehr man dazu liest.
Es gibt zig Wege, die Prinzipien zu erklären und zig Meinungen, wie man es interpretieren kann - mit der Zeit setzt sich dann ein Überblick zusammen und der ist sehr viel wert.
Bei mir hat's auch seine Zeit und verschiedene Sichtweisen gebraucht.


OldCat - So 12.12.21 15:41

Zitat:
user profile iconTh69Vllt. fehlt dir auch eher ein Grundverständnis der OOP? Du könntest dazu mal das OpenBook Objektorientierte Programmierung anschauen.

Da bin ich mir inzwischen sicher. Ich habe ooP nicht verstanden. In meinem ersten Thema hier im Forum kam doch auch schon der Verdacht auf, dass ich ooP nicht verstanden habe:

Verstehe das Keyword 'static' nicht eindeutig. [https://entwickler-ecke.de/viewtopic.php?t=118849]

Werde mir erstmal das Buch "Objektorientierte Programmierung von Bernhard Lahres, Gregor Rayman" durchlesen.


Palladin007 - So 12.12.21 16:00

Ich erinnere mich, dass die OOP-Erklärung im "Java ist auch eine Insel"-Buch sehr gut war. Das Buch geht natürlich noch sehr viel weiter, ich meine nur den OOP-Teil.
Ist aber keine Kaufempfehlung, doch wenn jemand in deinem Umfeld es hat - schau doch mal rein. Oder frag in der Schule/Berufsschule/Uni, irgendwer wird das schon haben.


OlafSt - Mo 13.12.21 09:19

Gerade was das Lernen angeht, habe ich in all den Jahren eine Sache festgestellt, die auch bei den allermeisten meiner Kollegen, aktuell wie vergangen, absolut zutreffend ist bzw. war:

Es geht nichts über selbst erarbeitetes Wissen. Dieser Moment, wo man gesagt bekommt "das wird so nicht funktionieren", man dann überlegt wieso nicht; herumprobiert;plötzlich die Lösung vor sich hat und das berühmte Licht im Kopf angeht: "Ja klar, darum geht das nicht" oder "jetzt kapier ich, wofür das gut ist" - das in diesem Moment gelernte gräbt sich um vieles tiefer in das Gedächtnis ein, als jede Zeile Code, die man liest, jede Zeile in einem Buch, jedes Video, das man sieht.

Darum schreibe ich auch nie die ganze Lösung hin - daraus lernt man nicht viel. Mein kleines Programm, das ich da für OldCat hingebastelt habe, sollte eben genau dies bewirken: Von dem, was ich da schrieb, ausgehend weiter herumprobieren. Was zu probieren ist, hatte ich schon geschrieben (nämlich das das nicht funktionieren wird).

Also sollte auf eigene Faust eine Methode implementiert werden, die es richtig macht. Und genau dieses "selbst implementieren" ist der Lerninhalt. Programmieren besteht nicht nur aus dem Befolgen von Formalien, von Syntax und Code-Styleguides. Der größte Teil daran ist kreativ - nämlich eine Lösung für ein Problem zu finden. Doch dies kann man nur schwer aus Büchern lernen.

Nach wie vor bin ich der Ansicht, ein Pair Programming, wo man OldCat mal zeigt, wie man an so ein Problem herangeht, sehr nützlich und hilfreich wäre. Ich denke nicht, das OldCat hier eine Hausaufgabe gelöst haben will, dafür dauert das schon zu lange ;)


OldCat - Di 14.12.21 10:06

Das ihr die Hausaufgaben für mich macht, ist ganz sicher nicht mein Anliegen. In keinem meiner Threads habe ich darum gebeten für mich ein Problem zu lösen. Sondern ich habe immer nur Fragen zu einem bestimmten Sachverhalt gehabt. Ich denke Fragen zu beantworten ist nicht "Hausaufgaben für andere zu machen".
Zwar hat Th69 für mich einen Taschenrechner programmiert und dafür bin ich ihm auch dankbar. Aber darum gebeten hatte ich im Umkehrschluss auch nicht. Dann stellt sich auch noch die Frage, ob Th69 wirklich meine Hausaufgaben gemacht hat. Ich denke nicht. Denn: Er hat mir lediglich eine Referenz zu meiner schon gemachten Hausafgabe (meinen Quellcode zum Thema) bereit gestellt.

Kurzum: Ich bin hier, um euch Fragen zu stellen, die ich gerne beantwortet haben würde, da Bücher nicht antworten. Bücher "reden" zwar, antworten aber nicht, wenn man eine Frage hat.
Ich bin hier, damit ihr mir was beibringt, aber nicht, damit ihr meine Hausaufgaben macht ;)

Zitat:
user profile iconOlafSt Nach wie vor bin ich der Ansicht, ein Pair Programming, wo man OldCat mal zeigt, wie man an so ein Problem herangeht, sehr nützlich und hilfreich wäre.
Sag mir wann und wo, und vor allem wie :mrgreen: und ich bin dabei.


OlafSt - Mi 15.12.21 08:47

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Das ihr die Hausaufgaben für mich macht, ist ganz sicher nicht mein Anliegen. In keinem meiner Threads habe ich darum gebeten für mich ein Problem zu lösen. Sondern ich habe immer nur Fragen zu einem bestimmten Sachverhalt gehabt. Ich denke Fragen zu beantworten ist nicht "Hausaufgaben für andere zu machen".


Du wärst überrascht, wie oft das vorkommt: Da wird lieblos irgendein Text hingerotzt, der vor Rechtschreibfehlern nur so strotzt. Dann ist es eine Aufgabe, die man mit 4 Stunden Videokurs auf Youtube lösen könnte. Und drunter steht dann plötzlich fehlerfrei: Wäre toll wenn ihr mir helfen könntet. Klassische "ich hab in Informatik 3 Wochen lang nur aufm Handy gedaddelt und jetzt blick ich nix"-Hausaufgabenbettelei. Hab ich bei dir nicht gesehen, nur deshalb habe ich überhaupt ein Wort geantwortet ;)

Zitat:

Zwar hat Th69 für mich einen Taschenrechner programmiert und dafür bin ich ihm auch dankbar. Aber darum gebeten hatte ich im Umkehrschluss auch nicht. Dann stellt sich auch noch die Frage, ob Th69 wirklich meine Hausaufgaben gemacht hat. Ich denke nicht. Denn: Er hat mir lediglich eine Referenz zu meiner schon gemachten Hausafgabe (meinen Quellcode zum Thema) bereit gestellt.


Ich denke, @Th69s Intention war eine andere: Nicht, dich loszuwerden, dafür braucht man sich nicht die Mühe zu machen und einen Taschenrechner programmieren :D Das geht einfacher. Ich denke eher, das sollte zur Anschauung dienen, wie man so ein Problem angeht und wie eine mögliche Lösung aussieht. Aus dieser kann man enorm viel herausziehen und lernen - ist aber schwierig, wenn viele der Basics noch fehlen.

Zitat:

Kurzum: Ich bin hier, um euch Fragen zu stellen, die ich gerne beantwortet haben würde, da Bücher nicht antworten. Bücher "reden" zwar, antworten aber nicht, wenn man eine Frage hat.
Ich bin hier, damit ihr mir was beibringt, aber nicht, damit ihr meine Hausaufgaben macht ;)

Darum reden wir ja noch mit Dir ;)

Zitat:
Zitat:
user profile iconOlafSt Nach wie vor bin ich der Ansicht, ein Pair Programming, wo man OldCat mal zeigt, wie man an so ein Problem herangeht, sehr nützlich und hilfreich wäre.
Sag mir wann und wo, und vor allem wie :mrgreen: und ich bin dabei.


Dazu schicke ich mal eine PN, wie ich mir das so vorstelle. Bitte beachten, wir alle haben ein Leben, also einen Job und private Verpflichtungen. Kann also einen oder zwei Tage dauern. In der Zwischenzeit findest du heraus, was PN ist und wie es funktioniert. Und du bringst dein Headset zum laufen, wenn es nicht wegen CoD oder WoW ohnehin schon funktioniert :D Cam wäre nett, ist aber nicht zwingend.


OldCat - Mi 15.12.21 18:31

Zitat:
Dazu schicke ich mal eine PN, wie ich mir das so vorstelle. Bitte beachten, wir alle haben ein Leben, also einen Job und private Verpflichtungen. Kann also einen oder zwei Tage dauern. In der Zwischenzeit findest du heraus, was PN ist und wie es funktioniert. Und du bringst dein Headset zum laufen, wenn es nicht wegen CoD oder WoW ohnehin schon funktioniert :D Cam wäre nett, ist aber nicht zwingend.


Eine PN an Dich ist raus gegangen. :dance2:


OldCat - Di 21.12.21 16:47

Hey ihr lieben :beer:

Derzeit bin ich ja damit geschäftigt, OOP noch einmal wesentlich genauer unter die Lupe zu nehmen. Ansich wollte ich mit Beiträgen in diesem Thread warten, bis ich mir alles angeeignet habe, was meine Bücher zum Thema OOP hergeben, bevor ich Fragen bezüglich des Quellcodes von Th69 stelle.

Dennoch wage ich einmal eine Frage, dessen Antwort ich in meinen Büchern nicht fand:

Im .cs Modul "Applikation.cs" hat user profile iconTh69 folgende Syntax geschrieben, die ich bis dato noch nie gesehen habe und auch daran scheitere, sie mir selbst zu erklären:

In dieser Datei schreibt Th69 zunächst die "Application Klasse".

Darunter schreibt Th69 eine UserInputs Klasse, die nur Felder definiert:


C#-Quelltext
1:
2:
3:
4:
5:
6:
public class UserInputs
        {
            public double Number1; { get; set; }
            public double Number2; { get; set; }
            public int Operator; { get; set; }
        }


Und jetzt kommt die Stelle, die ich nicht verstehe:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
 public static UserInputs Input()
        {
            Console.WriteLine("Please enter your first number: ");
            double number1 = double.Parse(Console.ReadLine());
            Console.WriteLine($"You chose: {number1}");

            Console.WriteLine("Please enter your second number: ");
            double number2 = double.Parse(Console.ReadLine());
            Console.WriteLine($"You chose: {number2}");

            Console.WriteLine("Choose your operator by number and hit enter:");
            Console.WriteLine("> 1.) Addition");
            Console.WriteLine("> 2.) Subtraction");
            Console.WriteLine("> 3.) Multiplication");
            Console.WriteLine("> 4.) Division");

            int op = 0;

            while (true)
            {
                op = int.Parse(Console.ReadLine());
                Console.WriteLine($"You chose: {op}");

                if (op < 1 || op > 4)
                {
                    Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
                    continue;
                }

                break;
            }

            return new UserInputs() { Number1 = number1, Number2 = number2, Operator = op };
        }


Folgendes verstehe ich nicht:


Ralf Jansen - Di 21.12.21 17:17

Zitat:
Was ist public static UserInputs Input()? Ist es eine statische Klasse, die eine Methode aufruft? Oder ist es eine statische Methode, die eine Klasse aufruft? Oder wie nennt man dieses Konstrukt?


Es ist eine Methode die eine Instanz vom Typ UserInputs zurückgibt.

Zitat:
Was genau macht: return new UserInputs() { Number1 = number1, Number2 = number2, Operator = op };? Gibt es eine neu erstellte Klasse zurück? Oder was macht es?


Es ist eine neu erstellte Instanz der Klasse UserInputs die dann sofort von der Methode zurückgegeben wird ja. Um keinen Konstruktor schreiben zu müssen zur Übergabe der Parameter oder eine weitere lokale Variable zu erfinden um die Instanz in mehreren Aufrufen zusammenzusetzen hat man hier einen sogenannten Initializer benutzt. Das in den geschweiften Klammern dahinter (der Initializer) ist die Zuweisung zu den einzelnen Properties der neuen Klasse und einfach nur weniger Schreibaufwand macht aber ansonsten das gleiche (abgesehen ein paar kleiner Details die aber jetzt nicht wichtig sind).

Moderiert von user profile iconTh69: C#-Tags (im Zitat) hinzugefügt


Palladin007 - Di 21.12.21 19:50

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Was genau macht: return new UserInputs() { Number1 = number1, Number2 = number2, Operator = op };? Gibt es eine neu erstellte Klasse zurück? Oder was macht es?

Vielleicht ein bisschen einfacher verständlich:


C#-Quelltext
1:
2:
3:
4:
5:
var result = new UserInputs();
result.Number1 = number1;
result.Number2 = number2;
result.Operator = op;
return result;

Das ist das gleiche wie:


C#-Quelltext
1:
return new UserInputs() { Number1 = number1, Number2 = number2, Operator = op };                    

Nur die Schreibweise ist eine Andere.

Ich persönlich mag die Schreibweise von Th69 lieber, weil die IDE dann "vergessene" Properties vorschlagen kann und alles mMn. etwas übersichtlicher ist.
Allerdings würde ich es umbrechen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
return new UserInputs()
{
    Number1 = number1,
    Number2 = number2,
    Operator = op
};

Auch das ist wieder das gleiche, nur anders formatiert.

Und noch eine Ergänzung:

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Darunter schreibt Th69 eine UserInputs Klasse, die nur Felder definiert:


C#-Quelltext
1:
2:
3:
4:
5:
6:
public class UserInputs
{
    public double Number1; { get; set; }
    public double Number2; { get; set; }
    public int Operator; { get; set; }
}

Das sind keine Felder sondern Properties (oder auf Deutsch "Eigenschaften").
Der Unterschied mag bei der Nutzung ziemlich irrelevant aussehen, ist technisch und für spätere Themen aber sehr wichtig und dahinter versteckt sich noch sehr viel mehr.
Wenn Du den Unterschied noch nicht kennst/verstehst, ist das erst mal nicht so wild, aber zumindest die Namen sollten passen, dann findest Du auch im Internet viel mehr.

Außerdem stimmt dein Code nicht, der würde so nicht kompilieren:


C#-Quelltext
1:
2:
3:
4:
5:
public class UserInputs
{
    public double Number1; { get; set; } // Falsch
    public double Number2 { get; set; } // Richtig
}


Das wären Felder oder Klassenvariablen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
public class UserInputs
{
    public double Number1;
    public double Number2;
    public int Operator;
}


Und das sind Properties:


C#-Quelltext
1:
2:
3:
4:
5:
6:
public class UserInputs
{
    public double Number1 { get; set; }
    public double Number2 { get; set; }
    public int Operator { get; set; }
}


OlafSt - Di 21.12.21 23:51

In dem Buch, das @OldCat da vor sich hat, ist das ganze sehr wahrscheinlich als "Attribute" zusammengefasst. Woraus sich schließen lässt, das der Autor des Buches aus der Java-Ecke stammt, das eben nur Attribute kennt, keine Properties und keine "richtigen" Getter/Setter, wie sie in C# definiert sind.


Palladin007 - Mi 22.12.21 10:48

Joa, die Vermutung passt auch zu den Aufgaben, die hier [https://entwickler-ecke.de/viewtopic.php?p=720062] gezeigt sind.
Für's OOP-Verständnis ist das nicht so wild, aber für C# reicht das nicht aus.


OldCat - Fr 07.01.22 11:42

Grüße in die Runde :beer:

Wollte mich nur zwischendurch hier mal melden. Hatte ja erzählt, ich habe mir ein Buch für lau gekauft, und arbeite das gerade durch. Bin noch nicht beim OOP Bereich angekommen, sondern lerne noch Grundlagen der Grundlagen (aus der Sicht des Buches), wie Dictionaries, Tupel, geschachtelte Schleifen, wie beispielsweise for-for oder foreach-for, und was man damit machen kann ...

Es wird noch ne Zeit dauern, bis ich meine neue Version des Taschenrechners präsentiere. Aktuell habe ich das Gefühl, ich habe noch viel zu lernen bis es so weit ist ...

Melde mich bald wieder.


Ralf Jansen - Fr 07.01.22 13:00

Zitat:
Aktuell habe ich das Gefühl, ich habe noch viel zu lernen bis es so weit ist ...

Eigentlich hast du ja einen halbwegs funktionierenden Taschenrechner ;) Funktional also ok aber strukturell verbesserungswürdig und wir haben versucht dir die Strukturmöglichkeiten des OOP nahezubringen.

Du wirst keinen Punkt erreichen an dem du soviel "Grundlagenwissen" angehäuft hast an dem du den ultimaten Taschenrechner schreiben wirst. Lernen endet nicht. Wenn ich jetzt einen schreiben müßte würde der den ich vielleicht nächstes Jahr schreibe schon wieder anders aussehen. Weil halt neues gelernt neue Erfahrung etc. Leider ist nicht garantiert das die Version des nächsten Jahres dann besser ist sonder einfach nur anders. Nun ja Leben ist hart oder wie man so sagt ;)

Worauf ich hinaus will. Während du Lektionen machst um die Grundlagen zu lernen versuche doch kurz zu überlegen ob du das Ergebnis dieser Lektion schon auf den Taschenrechner anwenden kannst und versuche das im Zweifel dann auch. Ich glaube ja immer noch das ins kalte Wasser schubsen und anwenden müssen (mit Hilfe) lehrreicher ist als Beispiellektionen nachturnen.

Zitat:
ich habe mir ein Buch für lau gekauft

Du bist hier in einem IT Forum insofern müssen wir hier auf logische Widersprüche hinweisen um unserer Natur zu entsprechen auch wenn wir natürlich das gesagte verstanden habe. "Für lau" und "gekauft" ist ein Widerspruch ;)


OldCat - Fr 07.01.22 16:55

Zitat:
user profile iconRalf JansenDu bist hier in einem IT Forum insofern müssen wir hier auf logische Widersprüche hinweisen um unserer Natur zu entsprechen auch wenn wir natürlich das gesagte verstanden habe. "Für lau" und "gekauft" ist ein Widerspruch


:mrgreen: ... in der Tat! Jahrzehnte falsches deutsch gesprochen und nie bemerkt :gruebel: Zu meiner Verteidigung muss ich dazu sagen, meine Mutter, die mich dereinst großzog und auch ihr Umfeld, hat den Begriff immer dann verwendet, wenn der Preis nicht heiß (=teuer) war. Eben nur lau ...

Danke Dir :mrgreen:

Wenn ich alles an OOP durchgearbeitet habe, was ich über OOP wissen sollte, wird meine neue Version des Taschenrechners kommen. Jetzt dürfte sich zum Thema OOP bei mir noch nicht viel getan haben...
Denn das war ja das große Problem.


Palladin007 - Fr 07.01.22 17:18

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Aktuell habe ich das Gefühl, ich habe noch viel zu lernen bis es so weit ist ...

Willkommen beim Job :P
Das gehört dazu und hört auch nie auf.

Aber die Grundlagen sind wie eine Werkzeug-Sammlung, Du wirst die Erklärungen, wie man eine Stichsäge benutzt, nicht verstehen können, ohne zu wissen, was eine Stichsäge ist.
Bei der OOP ist es allerdings etwas komplizierter, da es hier keine Werkzeug-Sammlung ist, die man nur richtig benutzen muss, sondern eine besondere Art, ein Problem anzugehen und zu lösen.
Also auch wenn Du das Buch durch hast, vielleicht sogar mehrfach, wirst Du unter Umständen nicht automatisch das Gefühl haben "jetzt hab ich's begriffen", das wird dauern.

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Während du Lektionen machst um die Grundlagen zu lernen versuche doch kurz zu überlegen ob du das Ergebnis dieser Lektion schon auf den Taschenrechner anwenden kannst und versuche das im Zweifel dann auch.

Das ist der entscheidende Punkt - wenn Du (Ralf) das immer meinst, bei Buch vs. andere Möglichkeiten, dann haben wir uns nie widersprochen :D
Ein Buch am Stück lesen und direkt alles mit zu nehmen, was geht, das wird vermutlich nur den absoluten Ausnahme-Talenten gelingen - falls überhaupt möglich.

Stattdessen ist es wichtig, jede Aussage in diesem Buch immer parallel auf etwas Reales (z.B. ein parallel bestehendes Projekt) zu beziehen und ggf. umzusetzen.
Du verstehst die Möglichkeiten und die OOP nicht (nur), weil das Buch es so gut erklärt, sondern weil Du das Erklärte ausprobierst und veränderst oder dagegen arbeitest und fest stellst, dass und warum es keine gute Idee ist und so weiter. Oder anders gesagt: Mach es falsch, lies im Buch, wie man es richtig macht und versuche das dann umzusetzen.

Ich bevorzuge allerdings Bücher (gegenüber 99% der Video-Tutorials), weil hinter Büchern meist ein sehr aufwändiger Plan und viel Erfahrung steckt. Naja - und weil früher oder später (eher früher) nur noch geschriebene Quellen gut genug sind, um fortgeschrittene Themen zu lernen.